diff --git a/packages/desktop-client/src/components/budget/index.tsx b/packages/desktop-client/src/components/budget/index.tsx
index 2d339b755ad300a649364630721bc8935a19841d..cad97746ba6d82c210c963d068153b00549495b9 100644
--- a/packages/desktop-client/src/components/budget/index.tsx
+++ b/packages/desktop-client/src/components/budget/index.tsx
@@ -39,7 +39,7 @@ import { DynamicBudgetTable } from './DynamicBudgetTable';
 import * as report from './report/ReportComponents';
 import { ReportProvider } from './report/ReportContext';
 import * as rollover from './rollover/RolloverComponents';
-import { RolloverContext } from './rollover/RolloverContext';
+import { RolloverProvider } from './rollover/RolloverContext';
 import { prewarmAllMonths, prewarmMonth, switchBudgetType } from './util';
 
 type ReportComponents = {
@@ -378,7 +378,7 @@ function BudgetInner(props: BudgetInnerProps) {
     );
   } else {
     table = (
-      <RolloverContext
+      <RolloverProvider
         summaryCollapsed={summaryCollapsed}
         onBudgetAction={onBudgetAction}
         onToggleSummaryCollapse={onToggleCollapse}
@@ -400,7 +400,7 @@ function BudgetInner(props: BudgetInnerProps) {
           onReorderCategory={onReorderCategory}
           onReorderGroup={onReorderGroup}
         />
-      </RolloverContext>
+      </RolloverProvider>
     );
   }
 
diff --git a/packages/desktop-client/src/components/budget/report/BalanceMenu.tsx b/packages/desktop-client/src/components/budget/report/BalanceMenu.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0878a98310bdd0f24f2d36312e8818778ac8142d
--- /dev/null
+++ b/packages/desktop-client/src/components/budget/report/BalanceMenu.tsx
@@ -0,0 +1,44 @@
+import React, { type ComponentPropsWithoutRef } from 'react';
+
+import { reportBudget } from 'loot-core/src/client/queries';
+
+import { Menu } from '../../common/Menu';
+import { useSheetValue } from '../../spreadsheet/useSheetValue';
+
+type BalanceMenuProps = Omit<
+  ComponentPropsWithoutRef<typeof Menu>,
+  'onMenuSelect' | 'items'
+> & {
+  categoryId: string;
+  onCarryover: (carryover: boolean) => void;
+};
+
+export function BalanceMenu({
+  categoryId,
+  onCarryover,
+  ...props
+}: BalanceMenuProps) {
+  const carryover = useSheetValue(reportBudget.catCarryover(categoryId));
+  return (
+    <Menu
+      {...props}
+      onMenuSelect={name => {
+        switch (name) {
+          case 'carryover':
+            onCarryover?.(!carryover);
+            break;
+          default:
+            throw new Error(`Unsupported item: ${name}`);
+        }
+      }}
+      items={[
+        {
+          name: 'carryover',
+          text: carryover
+            ? 'Remove overspending rollover'
+            : 'Rollover overspending',
+        },
+      ]}
+    />
+  );
+}
diff --git a/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx b/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx
index bf806d12b9067813f55c8cbf5b5309c41b21bc5f..66999627bf7800736b0683af2adeabb17325721d 100644
--- a/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx
+++ b/packages/desktop-client/src/components/budget/report/BalanceTooltip.tsx
@@ -1,11 +1,9 @@
 import React from 'react';
 
-import { reportBudget } from 'loot-core/src/client/queries';
-
-import { Menu } from '../../common/Menu';
-import { useSheetValue } from '../../spreadsheet/useSheetValue';
 import { Tooltip } from '../../tooltips';
 
+import { BalanceMenu } from './BalanceMenu';
+
 type BalanceTooltipProps = {
   categoryId: string;
   tooltip: { close: () => void };
@@ -22,8 +20,6 @@ export function BalanceTooltip({
   onClose,
   ...tooltipProps
 }: BalanceTooltipProps) {
-  const carryover = useSheetValue(reportBudget.catCarryover(categoryId));
-
   const _onClose = () => {
     tooltip.close();
     onClose?.();
@@ -37,22 +33,15 @@ export function BalanceTooltip({
       onClose={_onClose}
       {...tooltipProps}
     >
-      <Menu
-        onMenuSelect={() => {
-          onBudgetAction(monthIndex, 'carryover', {
+      <BalanceMenu
+        categoryId={categoryId}
+        onCarryover={carryover => {
+          onBudgetAction?.(monthIndex, 'carryover', {
             category: categoryId,
-            flag: !carryover,
+            flag: carryover,
           });
           _onClose();
         }}
-        items={[
-          {
-            name: 'carryover',
-            text: carryover
-              ? 'Remove overspending rollover'
-              : 'Rollover overspending',
-          },
-        ]}
       />
     </Tooltip>
   );
diff --git a/packages/desktop-client/src/components/budget/rollover/BalanceMenu.tsx b/packages/desktop-client/src/components/budget/rollover/BalanceMenu.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..803fcbe58b056f100bfabb632f0c8d503562ae48
--- /dev/null
+++ b/packages/desktop-client/src/components/budget/rollover/BalanceMenu.tsx
@@ -0,0 +1,67 @@
+import React, { type ComponentPropsWithoutRef } from 'react';
+
+import { rolloverBudget } from 'loot-core/src/client/queries';
+
+import { Menu } from '../../common/Menu';
+import { useSheetValue } from '../../spreadsheet/useSheetValue';
+
+type BalanceMenuProps = Omit<
+  ComponentPropsWithoutRef<typeof Menu>,
+  'onMenuSelect' | 'items'
+> & {
+  categoryId: string;
+  onTransfer: () => void;
+  onCarryover: (carryOver: boolean) => void;
+  onCover: () => void;
+};
+
+export function BalanceMenu({
+  categoryId,
+  onTransfer,
+  onCarryover,
+  onCover,
+  ...props
+}: BalanceMenuProps) {
+  const carryover = useSheetValue(rolloverBudget.catCarryover(categoryId));
+  const balance = useSheetValue(rolloverBudget.catBalance(categoryId));
+  return (
+    <Menu
+      {...props}
+      onMenuSelect={name => {
+        switch (name) {
+          case 'transfer':
+            onTransfer?.();
+            break;
+          case 'carryover':
+            onCarryover?.(!carryover);
+            break;
+          case 'cover':
+            onCover?.();
+            break;
+          default:
+            throw new Error(`Unsupported item: ${name}`);
+        }
+      }}
+      items={[
+        {
+          name: 'transfer',
+          text: 'Transfer to another category',
+        },
+        {
+          name: 'carryover',
+          text: carryover
+            ? 'Remove overspending rollover'
+            : 'Rollover overspending',
+        },
+        ...(balance < 0
+          ? [
+              {
+                name: 'cover',
+                text: 'Cover overspending',
+              },
+            ]
+          : []),
+      ]}
+    />
+  );
+}
diff --git a/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx b/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx
index 5ad6251205aa7ff62e99458dad60e1edff171be9..66b8f86ba30538d0de14b7dd7e1c597b4bb744ed 100644
--- a/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/BalanceTooltip.tsx
@@ -2,10 +2,10 @@ import React, { useState } from 'react';
 
 import { rolloverBudget } from 'loot-core/src/client/queries';
 
-import { Menu } from '../../common/Menu';
 import { useSheetValue } from '../../spreadsheet/useSheetValue';
 import { Tooltip } from '../../tooltips';
 
+import { BalanceMenu } from './BalanceMenu';
 import { CoverTooltip } from './CoverTooltip';
 import { TransferTooltip } from './TransferTooltip';
 
@@ -16,6 +16,7 @@ type BalanceTooltipProps = {
   onBudgetAction: (idx: number, action: string, arg?: unknown) => void;
   onClose?: () => void;
 };
+
 export function BalanceTooltip({
   categoryId,
   tooltip,
@@ -24,8 +25,7 @@ export function BalanceTooltip({
   onClose,
   ...tooltipProps
 }: BalanceTooltipProps) {
-  const carryover = useSheetValue(rolloverBudget.catCarryover(categoryId));
-  const balance = useSheetValue(rolloverBudget.catBalance(categoryId));
+  const catBalance = useSheetValue(rolloverBudget.catBalance(categoryId));
   const [menu, setMenu] = useState('menu');
 
   const _onClose = () => {
@@ -43,52 +43,31 @@ export function BalanceTooltip({
           onClose={_onClose}
           {...tooltipProps}
         >
-          <Menu
-            onMenuSelect={type => {
-              if (type === 'carryover') {
-                onBudgetAction(monthIndex, 'carryover', {
-                  category: categoryId,
-                  flag: !carryover,
-                });
-                _onClose();
-              } else {
-                setMenu(type);
-              }
+          <BalanceMenu
+            categoryId={categoryId}
+            onCarryover={carryover => {
+              onBudgetAction(monthIndex, 'carryover', {
+                category: categoryId,
+                flag: carryover,
+              });
+              _onClose();
             }}
-            items={[
-              {
-                name: 'transfer',
-                text: 'Transfer to another category',
-              },
-              {
-                name: 'carryover',
-                text: carryover
-                  ? 'Remove overspending rollover'
-                  : 'Rollover overspending',
-              },
-              ...(balance < 0
-                ? [
-                    {
-                      name: 'cover',
-                      text: 'Cover overspending',
-                    },
-                  ]
-                : []),
-            ]}
+            onTransfer={() => setMenu('transfer')}
+            onCover={() => setMenu('cover')}
           />
         </Tooltip>
       )}
 
       {menu === 'transfer' && (
         <TransferTooltip
-          initialAmountName={rolloverBudget.catBalance(categoryId)}
+          initialAmount={catBalance}
           showToBeBudgeted={true}
           onClose={_onClose}
-          onSubmit={(amount, toCategory) => {
+          onSubmit={(amount, toCategoryId) => {
             onBudgetAction(monthIndex, 'transfer-category', {
               amount,
               from: categoryId,
-              to: toCategory,
+              to: toCategoryId,
             });
           }}
         />
@@ -97,10 +76,10 @@ export function BalanceTooltip({
       {menu === 'cover' && (
         <CoverTooltip
           onClose={_onClose}
-          onSubmit={fromCategory => {
+          onSubmit={fromCategoryId => {
             onBudgetAction(monthIndex, 'cover', {
               to: categoryId,
-              from: fromCategory,
+              from: fromCategoryId,
             });
           }}
         />
diff --git a/packages/desktop-client/src/components/budget/rollover/CoverTooltip.tsx b/packages/desktop-client/src/components/budget/rollover/CoverTooltip.tsx
index 590fe23e773d2f22bd07522a8985a7f19d36aa96..60458e71e8ecfb033bab8f6b0bf8d60b0ca4b798 100644
--- a/packages/desktop-client/src/components/budget/rollover/CoverTooltip.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/CoverTooltip.tsx
@@ -10,7 +10,7 @@ import { addToBeBudgetedGroup } from '../util';
 
 type CoverTooltipProps = {
   tooltipProps?: ComponentProps<typeof Tooltip>;
-  onSubmit: (category: unknown) => void;
+  onSubmit: (categoryId: string) => void;
   onClose: () => void;
 };
 export function CoverTooltip({
@@ -18,18 +18,10 @@ export function CoverTooltip({
   onSubmit,
   onClose,
 }: CoverTooltipProps) {
-  const { grouped } = useCategories();
-  const categoryGroups = addToBeBudgetedGroup(
-    grouped.filter(g => !g.is_income),
-  );
-  const [category, setCategory] = useState<string | null>(null);
-
-  function submit() {
-    if (category) {
-      onSubmit(category);
-      onClose();
-    }
-  }
+  const _onSubmit = (categoryId: string) => {
+    onSubmit?.(categoryId);
+    onClose?.();
+  };
 
   return (
     <Tooltip
@@ -39,16 +31,38 @@ export function CoverTooltip({
       {...tooltipProps}
       onClose={onClose}
     >
+      <Cover onSubmit={_onSubmit} />
+    </Tooltip>
+  );
+}
+
+type CoverProps = {
+  onSubmit: (categoryId: string) => void;
+};
+
+function Cover({ onSubmit }: CoverProps) {
+  const { grouped: originalCategoryGroups } = useCategories();
+  const categoryGroups = addToBeBudgetedGroup(
+    originalCategoryGroups.filter(g => !g.is_income),
+  );
+  const [categoryId, setCategoryId] = useState<string | null>(null);
+
+  function submit() {
+    if (categoryId) {
+      onSubmit(categoryId);
+    }
+  }
+  return (
+    <>
       <View style={{ marginBottom: 5 }}>Cover from category:</View>
 
       <InitialFocus>
         {node => (
           <CategoryAutocomplete
             categoryGroups={categoryGroups}
-            value={null}
+            value={categoryGroups.find(g => g.id === categoryId)}
             openOnFocus={true}
-            onUpdate={() => {}}
-            onSelect={(id: string | undefined) => setCategory(id || null)}
+            onSelect={(id: string | undefined) => setCategoryId(id || null)}
             inputProps={{
               inputRef: node,
               onKeyDown: e => {
@@ -56,6 +70,7 @@ export function CoverTooltip({
                   submit();
                 }
               },
+              placeholder: '(none)',
             }}
             showHiddenCategories={false}
           />
@@ -79,6 +94,6 @@ export function CoverTooltip({
           Transfer
         </Button>
       </View>
-    </Tooltip>
+    </>
   );
 }
diff --git a/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx b/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx
index 583444a4716b294025c061a23a4d5dabfff84c5d..ea05cd8737444ee10c9e1b6a6f1e1c6b1c7813da 100644
--- a/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/RolloverContext.tsx
@@ -22,15 +22,15 @@ const Context = createContext<RolloverContextDefinition>({
   currentMonth: 'unknown',
 });
 
-type RolloverContextProps = Omit<RolloverContextDefinition, 'currentMonth'> & {
+type RolloverProviderProps = Omit<RolloverContextDefinition, 'currentMonth'> & {
   children: ReactNode;
 };
-export function RolloverContext({
+export function RolloverProvider({
   summaryCollapsed,
   onBudgetAction,
   onToggleSummaryCollapse,
   children,
-}: RolloverContextProps) {
+}: RolloverProviderProps) {
   const currentMonth = monthUtils.currentMonth();
 
   return (
diff --git a/packages/desktop-client/src/components/budget/rollover/TransferTooltip.tsx b/packages/desktop-client/src/components/budget/rollover/TransferTooltip.tsx
index d321eea3aa3fbb1bc9f24c9295225b4ea101cb0d..f23e53ca5f1eaf82bb0cee77d9396f80dcaffaf2 100644
--- a/packages/desktop-client/src/components/budget/rollover/TransferTooltip.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/TransferTooltip.tsx
@@ -1,11 +1,6 @@
-import React, {
-  useState,
-  useContext,
-  useEffect,
-  type ComponentPropsWithoutRef,
-} from 'react';
+import type React from 'react';
+import { useState, type ComponentPropsWithoutRef } from 'react';
 
-import { useSpreadsheet } from 'loot-core/src/client/SpreadsheetProvider';
 import { evalArithmetic } from 'loot-core/src/shared/arithmetic';
 import { integerToCurrency, amountToInteger } from 'loot-core/src/shared/util';
 
@@ -15,79 +10,78 @@ import { Button } from '../../common/Button';
 import { InitialFocus } from '../../common/InitialFocus';
 import { Input } from '../../common/Input';
 import { View } from '../../common/View';
-import { NamespaceContext } from '../../spreadsheet/NamespaceContext';
 import { Tooltip } from '../../tooltips';
 import { addToBeBudgetedGroup } from '../util';
 
 type TransferTooltipProps = ComponentPropsWithoutRef<typeof Tooltip> & {
   initialAmount?: number;
-  initialAmountName?: string;
   showToBeBudgeted?: boolean;
-  onSubmit: (amount: number, category: string) => void;
+  onSubmit: (amount: number, categoryId: string) => void;
 };
+
 export function TransferTooltip({
   initialAmount = 0,
-  initialAmountName,
   showToBeBudgeted,
   onSubmit,
   onClose,
   position = 'bottom-right',
   ...props
 }: TransferTooltipProps) {
-  const spreadsheet = useSpreadsheet();
-  const sheetName = useContext(NamespaceContext);
-  let { grouped: categoryGroups } = useCategories();
+  const _onSubmit = (amount: number, categoryId: string) => {
+    onSubmit?.(amount, categoryId);
+    onClose?.();
+  };
+
+  return (
+    <Tooltip
+      position={position}
+      width={200}
+      style={{ padding: 10 }}
+      onClose={onClose}
+      {...props}
+    >
+      <Transfer amount={initialAmount} showToBeBudgeted onSubmit={_onSubmit} />
+    </Tooltip>
+  );
+}
+
+type TransferProps = {
+  amount: number;
+  showToBeBudgeted: boolean;
+  onSubmit: (amount: number, categoryId: string) => void;
+};
 
-  categoryGroups = categoryGroups.filter(g => !g.is_income);
+function Transfer({
+  amount: initialAmount,
+  showToBeBudgeted,
+  onSubmit,
+}: TransferProps) {
+  const { grouped: originalCategoryGroups } = useCategories();
+  let categoryGroups = originalCategoryGroups.filter(g => !g.is_income);
   if (showToBeBudgeted) {
     categoryGroups = addToBeBudgetedGroup(categoryGroups);
   }
 
+  const _initialAmount = integerToCurrency(Math.max(initialAmount, 0));
   const [amount, setAmount] = useState<string | null>(null);
-  const [category, setCategory] = useState<string | null>(null);
+  const [categoryId, setCategoryId] = useState<string | null>(null);
 
-  useEffect(() => {
-    (async () => {
-      if (initialAmountName) {
-        const node = await spreadsheet.get(sheetName, initialAmountName);
-        setAmount(integerToCurrency(Math.max(node.value as number, 0)));
-      } else {
-        setAmount(integerToCurrency(Math.max(initialAmount, 0)));
-      }
-    })();
-  }, []);
-
-  function submit(newAmount: string) {
-    const parsedAmount = evalArithmetic(newAmount);
-    if (parsedAmount && category) {
-      onSubmit(amountToInteger(parsedAmount), category);
-      onClose();
+  const _onSubmit = (newAmount: string | null, categoryId: string | null) => {
+    const parsedAmount = evalArithmetic(newAmount || '');
+    if (parsedAmount && categoryId) {
+      onSubmit?.(amountToInteger(parsedAmount), categoryId);
     }
-  }
-
-  if (amount === null) {
-    // Don't render anything until we have the amount to show. This
-    // ensures that the amount field is focused and fully selected
-    // when it's initially rendered (instead of being updated
-    // afterwards and losing selection)
-    return null;
-  }
+  };
 
   return (
-    <Tooltip
-      position={position}
-      width={200}
-      style={{ padding: 10 }}
-      onClose={onClose}
-      {...props}
-    >
+    <>
       <View style={{ marginBottom: 5 }}>Transfer this amount:</View>
       <View>
         <InitialFocus>
           <Input
-            value={amount}
-            onChange={e => setAmount(e.target['value'])}
-            onEnter={() => submit(amount)}
+            defaultValue={_initialAmount}
+            onUpdate={value => setAmount(value)}
+            onEnter={() => _onSubmit(amount, categoryId)}
           />
         </InitialFocus>
       </View>
@@ -95,11 +89,13 @@ export function TransferTooltip({
 
       <CategoryAutocomplete
         categoryGroups={categoryGroups}
-        value={null}
+        value={categoryGroups.find(g => g.id === categoryId)}
         openOnFocus={true}
-        onUpdate={() => {}}
-        onSelect={(id: string | undefined) => setCategory(id || null)}
-        inputProps={{ onEnter: () => submit(amount), placeholder: '(none)' }}
+        onSelect={(id: string | undefined) => setCategoryId(id || null)}
+        inputProps={{
+          onEnter: () => _onSubmit(amount, categoryId),
+          placeholder: '(none)',
+        }}
         showHiddenCategories={true}
       />
 
@@ -116,11 +112,11 @@ export function TransferTooltip({
             paddingTop: 3,
             paddingBottom: 3,
           }}
-          onClick={() => submit(amount)}
+          onClick={() => _onSubmit(amount, categoryId)}
         >
           Transfer
         </Button>
       </View>
-    </Tooltip>
+    </>
   );
 }
diff --git a/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx
index 02b1d1da6a4d4cae1a964de490a0ba11d9485161..75051811aa0ec9729935bfa66519f0ecbc5ec953 100644
--- a/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx
+++ b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudget.tsx
@@ -1,23 +1,15 @@
 import React, { useState, type ComponentPropsWithoutRef } from 'react';
 
-import { css } from 'glamor';
-
 import { rolloverBudget } from 'loot-core/src/client/queries';
 
-import { theme, styles, type CSSProperties } from '../../../../style';
-import { Block } from '../../../common/Block';
-import { HoverTarget } from '../../../common/HoverTarget';
-import { Menu } from '../../../common/Menu';
-import { View } from '../../../common/View';
-import { PrivacyFilter } from '../../../PrivacyFilter';
-import { useFormat } from '../../../spreadsheet/useFormat';
-import { useSheetName } from '../../../spreadsheet/useSheetName';
+import { type CSSProperties } from '../../../../style';
 import { useSheetValue } from '../../../spreadsheet/useSheetValue';
 import { Tooltip } from '../../../tooltips';
 import { HoldTooltip } from '../HoldTooltip';
 import { TransferTooltip } from '../TransferTooltip';
 
-import { TotalsList } from './TotalsList';
+import { ToBudgetAmount } from './ToBudgetAmount';
+import { ToBudgetMenu } from './ToBudgetMenu';
 
 type ToBudgetProps = {
   month: string;
@@ -44,116 +36,62 @@ export function ToBudget({
   transferTooltipProps,
 }: ToBudgetProps) {
   const [menuOpen, setMenuOpen] = useState<string | null>(null);
-  const sheetName = useSheetName(rolloverBudget.toBudget);
   const sheetValue = useSheetValue({
     name: rolloverBudget.toBudget,
     value: 0,
   });
-  const format = useFormat();
   const availableValue = parseInt(sheetValue);
-  const num = isNaN(availableValue) ? 0 : availableValue;
-  const isNegative = num < 0;
 
   return (
-    <View style={{ alignItems: 'center', ...style }}>
-      <Block>{isNegative ? 'Overbudgeted:' : 'To Budget:'}</Block>
-      <View>
-        <HoverTarget
-          disabled={!showTotalsTooltipOnHover || !!menuOpen}
-          renderContent={() => (
-            <Tooltip position="bottom-center" {...totalsTooltipProps}>
-              <TotalsList
-                prevMonthName={prevMonthName}
-                style={{
-                  padding: 7,
-                }}
-              />
-            </Tooltip>
-          )}
+    <>
+      <ToBudgetAmount
+        onClick={() => setMenuOpen('actions')}
+        prevMonthName={prevMonthName}
+        showTotalsTooltipOnHover={showTotalsTooltipOnHover}
+        totalsTooltipProps={totalsTooltipProps}
+        style={style}
+        amountStyle={amountStyle}
+      />
+      {menuOpen === 'actions' && (
+        <Tooltip
+          position="bottom-center"
+          width={200}
+          style={{ padding: 0 }}
+          onClose={() => setMenuOpen(null)}
+          {...menuTooltipProps}
         >
-          <PrivacyFilter blurIntensity={7}>
-            <Block
-              onClick={() => setMenuOpen('actions')}
-              data-cellname={sheetName}
-              className={`${css([
-                styles.veryLargeText,
-                {
-                  fontWeight: 400,
-                  userSelect: 'none',
-                  cursor: 'pointer',
-                  color: isNegative ? theme.errorText : theme.pageTextPositive,
-                  marginBottom: -1,
-                  borderBottom: '1px solid transparent',
-                  ':hover': {
-                    borderColor: isNegative
-                      ? theme.errorBorder
-                      : theme.pageTextPositive,
-                  },
-                },
-                amountStyle,
-              ])}`}
-            >
-              {format(num, 'financial')}
-            </Block>
-          </PrivacyFilter>
-        </HoverTarget>
-        {menuOpen === 'actions' && (
-          <Tooltip
-            position="bottom-center"
-            width={200}
-            style={{ padding: 0 }}
-            onClose={() => setMenuOpen(null)}
-            {...menuTooltipProps}
-          >
-            <Menu
-              onMenuSelect={type => {
-                if (type === 'reset-buffer') {
-                  onBudgetAction(month, 'reset-hold');
-                  setMenuOpen(null);
-                } else {
-                  setMenuOpen(type);
-                }
-              }}
-              items={[
-                {
-                  name: 'transfer',
-                  text: 'Move to a category',
-                },
-                {
-                  name: 'buffer',
-                  text: 'Hold for next month',
-                },
-                {
-                  name: 'reset-buffer',
-                  text: 'Reset next month’s buffer',
-                },
-              ]}
-            />
-          </Tooltip>
-        )}
-        {menuOpen === 'buffer' && (
-          <HoldTooltip
-            onClose={() => setMenuOpen(null)}
-            onSubmit={amount => {
-              onBudgetAction(month, 'hold', { amount });
-            }}
-            {...holdTooltipProps}
-          />
-        )}
-        {menuOpen === 'transfer' && (
-          <TransferTooltip
-            initialAmount={availableValue}
-            onClose={() => setMenuOpen(null)}
-            onSubmit={(amount, category) => {
-              onBudgetAction(month, 'transfer-available', {
-                amount,
-                category,
-              });
+          <ToBudgetMenu
+            onTransfer={() => setMenuOpen('transfer')}
+            onHoldBuffer={() => setMenuOpen('buffer')}
+            onResetHoldBuffer={() => {
+              onBudgetAction(month, 'reset-hold');
+              setMenuOpen(null);
             }}
-            {...transferTooltipProps}
           />
-        )}
-      </View>
-    </View>
+        </Tooltip>
+      )}
+      {menuOpen === 'buffer' && (
+        <HoldTooltip
+          onClose={() => setMenuOpen(null)}
+          onSubmit={amount => {
+            onBudgetAction(month, 'hold', { amount });
+          }}
+          {...holdTooltipProps}
+        />
+      )}
+      {menuOpen === 'transfer' && (
+        <TransferTooltip
+          initialAmount={availableValue}
+          onClose={() => setMenuOpen(null)}
+          onSubmit={(amount, category) => {
+            onBudgetAction(month, 'transfer-available', {
+              amount,
+              category,
+            });
+          }}
+          {...transferTooltipProps}
+        />
+      )}
+    </>
   );
 }
diff --git a/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudgetAmount.tsx b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudgetAmount.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4764903c74b7f230f4211ee94bc4177800e6a980
--- /dev/null
+++ b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudgetAmount.tsx
@@ -0,0 +1,92 @@
+import React, { type ComponentPropsWithoutRef } from 'react';
+
+import { css } from 'glamor';
+
+import { rolloverBudget } from 'loot-core/src/client/queries';
+
+import { theme, styles, type CSSProperties } from '../../../../style';
+import { Block } from '../../../common/Block';
+import { HoverTarget } from '../../../common/HoverTarget';
+import { View } from '../../../common/View';
+import { PrivacyFilter } from '../../../PrivacyFilter';
+import { useFormat } from '../../../spreadsheet/useFormat';
+import { useSheetName } from '../../../spreadsheet/useSheetName';
+import { useSheetValue } from '../../../spreadsheet/useSheetValue';
+import { Tooltip } from '../../../tooltips';
+
+import { TotalsList } from './TotalsList';
+
+type ToBudgetAmountProps = {
+  prevMonthName: string;
+  showTotalsTooltipOnHover?: boolean;
+  totalsTooltipProps?: ComponentPropsWithoutRef<typeof Tooltip>;
+  style?: CSSProperties;
+  amountStyle?: CSSProperties;
+  onClick: () => void;
+};
+
+export function ToBudgetAmount({
+  prevMonthName,
+  showTotalsTooltipOnHover,
+  totalsTooltipProps,
+  style,
+  amountStyle,
+  onClick,
+}: ToBudgetAmountProps) {
+  const sheetName = useSheetName(rolloverBudget.toBudget);
+  const sheetValue = useSheetValue({
+    name: rolloverBudget.toBudget,
+    value: 0,
+  });
+  const format = useFormat();
+  const availableValue = parseInt(sheetValue);
+  const num = isNaN(availableValue) ? 0 : availableValue;
+  const isNegative = num < 0;
+
+  return (
+    <View style={{ alignItems: 'center', ...style }}>
+      <Block>{isNegative ? 'Overbudgeted:' : 'To Budget:'}</Block>
+      <View>
+        <HoverTarget
+          disabled={!showTotalsTooltipOnHover}
+          renderContent={() => (
+            <Tooltip position="bottom-center" {...totalsTooltipProps}>
+              <TotalsList
+                prevMonthName={prevMonthName}
+                style={{
+                  padding: 7,
+                }}
+              />
+            </Tooltip>
+          )}
+        >
+          <PrivacyFilter blurIntensity={7}>
+            <Block
+              onClick={onClick}
+              data-cellname={sheetName}
+              className={`${css([
+                styles.veryLargeText,
+                {
+                  fontWeight: 400,
+                  userSelect: 'none',
+                  cursor: 'pointer',
+                  color: isNegative ? theme.errorText : theme.pageTextPositive,
+                  marginBottom: -1,
+                  borderBottom: '1px solid transparent',
+                  ':hover': {
+                    borderColor: isNegative
+                      ? theme.errorBorder
+                      : theme.pageTextPositive,
+                  },
+                },
+                amountStyle,
+              ])}`}
+            >
+              {format(num, 'financial')}
+            </Block>
+          </PrivacyFilter>
+        </HoverTarget>
+      </View>
+    </View>
+  );
+}
diff --git a/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudgetMenu.tsx b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudgetMenu.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..183e7d28753c9ee11a208839b19bf924a10795b2
--- /dev/null
+++ b/packages/desktop-client/src/components/budget/rollover/budgetsummary/ToBudgetMenu.tsx
@@ -0,0 +1,53 @@
+import React, { type ComponentPropsWithoutRef } from 'react';
+
+import { Menu } from '../../../common/Menu';
+
+type ToBudgetMenuProps = Omit<
+  ComponentPropsWithoutRef<typeof Menu>,
+  'onMenuSelect' | 'items'
+> & {
+  onTransfer: () => void;
+  onHoldBuffer: () => void;
+  onResetHoldBuffer: () => void;
+};
+export function ToBudgetMenu({
+  onTransfer,
+  onHoldBuffer,
+  onResetHoldBuffer,
+  ...props
+}: ToBudgetMenuProps) {
+  return (
+    <Menu
+      {...props}
+      onMenuSelect={name => {
+        switch (name) {
+          case 'transfer':
+            onTransfer?.();
+            break;
+          case 'buffer':
+            onHoldBuffer?.();
+            break;
+          case 'reset-buffer':
+            onResetHoldBuffer?.();
+            break;
+          default:
+            throw new Error(`Unsupported item: ${name}`);
+        }
+      }}
+      items={[
+        {
+          name: 'transfer',
+          text: 'Move to a category',
+        },
+        {
+          name: 'buffer',
+          text: 'Hold for next month',
+        },
+        {
+          name: 'reset-buffer',
+          text: 'Reset next month’s buffer',
+        },
+      ]}
+    />
+  );
+}
diff --git a/packages/desktop-client/src/components/common/Menu.tsx b/packages/desktop-client/src/components/common/Menu.tsx
index 4d5ed8691dc9cc9bbdfc9ed473b903bd7c8edc31..a948ca5ec4e086d83c25e1ed2b06f4f67cb6b99d 100644
--- a/packages/desktop-client/src/components/common/Menu.tsx
+++ b/packages/desktop-client/src/components/common/Menu.tsx
@@ -1,11 +1,11 @@
 import {
-  type FunctionComponent,
   type ReactElement,
   type ReactNode,
-  createElement,
   useEffect,
   useRef,
   useState,
+  type ComponentType,
+  type SVGProps,
 } from 'react';
 
 import { type CSSProperties, theme } from '../../style';
@@ -34,16 +34,10 @@ type MenuItem = {
   type?: string | symbol;
   name: string;
   disabled?: boolean;
-  // eslint-disable-next-line @typescript-eslint/ban-types
-  icon?: FunctionComponent<{
-    width: number;
-    height: number;
-    style: CSSProperties;
-  }>;
+  icon?: ComponentType<SVGProps<SVGSVGElement>>;
   iconSize?: number;
   text: string;
   key?: string;
-  style?: CSSProperties;
   toggle?: boolean;
   tooltip?: string;
 };
@@ -54,6 +48,7 @@ type MenuProps<T extends MenuItem = MenuItem> = {
   items: Array<T | typeof Menu.line>;
   onMenuSelect?: (itemName: T['name']) => void;
   style?: CSSProperties;
+  getItemStyle?: (item: T) => CSSProperties;
 };
 
 export function Menu<T extends MenuItem>({
@@ -62,6 +57,7 @@ export function Menu<T extends MenuItem>({
   items: allItems,
   onMenuSelect,
   style,
+  getItemStyle,
 }: MenuProps<T>) {
   const elRef = useRef<HTMLDivElement>(null);
   const items = allItems.filter(x => x);
@@ -149,6 +145,7 @@ export function Menu<T extends MenuItem>({
         }
 
         const lastItem = items[idx - 1];
+        const Icon = item.icon;
 
         return (
           <View
@@ -172,31 +169,22 @@ export function Menu<T extends MenuItem>({
                   backgroundColor: theme.menuItemBackgroundHover,
                   color: theme.menuItemTextHover,
                 }),
-              ...item.style,
+              ...getItemStyle?.(item),
             }}
-            onMouseEnter={() => setHoveredIndex(idx)}
-            onMouseLeave={() => setHoveredIndex(null)}
-            onClick={() =>
-              !item.disabled &&
-              onMenuSelect &&
-              item.toggle === undefined &&
-              onMenuSelect(item.name)
-            }
+            onPointerEnter={() => setHoveredIndex(idx)}
+            onPointerLeave={() => setHoveredIndex(null)}
+            onClick={() => !item.disabled && onMenuSelect?.(item.name)}
           >
             {/* Force it to line up evenly */}
             {item.toggle === undefined ? (
               <>
-                <Text style={{ lineHeight: 0 }}>
-                  {item.icon &&
-                    createElement(item.icon, {
-                      width: item.iconSize || 10,
-                      height: item.iconSize || 10,
-                      style: {
-                        marginRight: 7,
-                        width: item.iconSize || 10,
-                      },
-                    })}
-                </Text>
+                {Icon && (
+                  <Icon
+                    width={item.iconSize || 10}
+                    height={item.iconSize || 10}
+                    style={{ marginRight: 7, width: item.iconSize || 10 }}
+                  />
+                )}
                 <Text title={item.tooltip}>{item.text}</Text>
                 <View style={{ flex: 1 }} />
               </>
@@ -210,12 +198,9 @@ export function Menu<T extends MenuItem>({
                   id={item.name}
                   checked={item.toggle}
                   onColor={theme.pageTextPositive}
-                  style={{ marginLeft: 5, ...item.style }}
+                  style={{ marginLeft: 5 }}
                   onToggle={() =>
-                    !item.disabled &&
-                    onMenuSelect &&
-                    item.toggle !== undefined &&
-                    onMenuSelect(item.name)
+                    !item.disabled && item.toggle && onMenuSelect?.(item.name)
                   }
                 />
               </>
diff --git a/packages/desktop-client/src/components/mobile/MobileForms.jsx b/packages/desktop-client/src/components/mobile/MobileForms.jsx
deleted file mode 100644
index e3f46e0b1d424593ff5f33b7127757b3db179e3f..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/components/mobile/MobileForms.jsx
+++ /dev/null
@@ -1,150 +0,0 @@
-import { forwardRef } from 'react';
-
-import { css } from 'glamor';
-
-import { theme, styles } from '../../style';
-import { Button } from '../common/Button';
-import { Input } from '../common/Input';
-import { Text } from '../common/Text';
-import { View } from '../common/View';
-
-const FIELD_HEIGHT = 40;
-
-export function FieldLabel({ title, flush, style }) {
-  return (
-    <Text
-      style={{
-        marginBottom: 5,
-        marginTop: flush ? 0 : 25,
-        fontSize: 13,
-        color: theme.tableRowHeaderText,
-        padding: `0 ${styles.mobileEditingPadding}px`,
-        textTransform: 'uppercase',
-        userSelect: 'none',
-        ...style,
-      }}
-    >
-      {title}
-    </Text>
-  );
-}
-
-const valueStyle = {
-  borderWidth: 1,
-  borderColor: theme.formInputBorder,
-  marginLeft: 8,
-  marginRight: 8,
-  height: FIELD_HEIGHT,
-};
-
-export const InputField = forwardRef(function InputField(
-  { disabled, style, onUpdate, ...props },
-  ref,
-) {
-  return (
-    <Input
-      inputRef={ref}
-      autoCorrect="false"
-      autoCapitalize="none"
-      disabled={disabled}
-      onUpdate={onUpdate}
-      style={{
-        ...valueStyle,
-        ...style,
-        color: disabled ? theme.tableTextInactive : theme.tableText,
-        backgroundColor: disabled
-          ? theme.formInputTextReadOnlySelection
-          : theme.tableBackground,
-      }}
-      {...props}
-    />
-  );
-});
-
-export function TapField({
-  value,
-  children,
-  disabled,
-  rightContent,
-  style,
-  textStyle,
-  onClick,
-  ...props
-}) {
-  return (
-    <Button
-      as={View}
-      onClick={!disabled ? onClick : undefined}
-      style={{
-        flexDirection: 'row',
-        alignItems: 'center',
-        ...style,
-        ...valueStyle,
-        backgroundColor: theme.tableBackground,
-        ...(disabled && {
-          backgroundColor: theme.formInputTextReadOnlySelection,
-        }),
-      }}
-      bounce={false}
-      activeStyle={{
-        opacity: 0.5,
-        boxShadow: 'none',
-      }}
-      hoveredStyle={{
-        boxShadow: 'none',
-      }}
-      // activeOpacity={0.05}
-      {...props}
-    >
-      {children ? (
-        children
-      ) : (
-        <Text style={{ flex: 1, userSelect: 'none', ...textStyle }}>
-          {value}
-        </Text>
-      )}
-      {!disabled && rightContent}
-    </Button>
-  );
-}
-
-export function BooleanField({ checked, onUpdate, style, disabled = false }) {
-  return (
-    <input
-      disabled={disabled ? true : undefined}
-      type="checkbox"
-      checked={checked}
-      onChange={e => onUpdate(e.target.checked)}
-      className={`${css([
-        {
-          marginInline: styles.mobileEditingPadding,
-          flexShrink: 0,
-          appearance: 'none',
-          outline: 0,
-          border: '1px solid ' + theme.formInputBorder,
-          borderRadius: 4,
-          display: 'flex',
-          alignItems: 'center',
-          justifyContent: 'center',
-          color: theme.checkboxText,
-          backgroundColor: theme.tableBackground,
-          ':checked': {
-            border: '1px solid ' + theme.checkboxBorderSelected,
-            backgroundColor: theme.checkboxBackgroundSelected,
-            '::after': {
-              display: 'block',
-              background:
-                theme.checkboxBackgroundSelected +
-                // eslint-disable-next-line rulesdir/typography
-                ' url(\'data:image/svg+xml; utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="white" d="M0 11l2-2 5 5L18 3l2 2L7 18z"/></svg>\') 15px 15px',
-              width: 15,
-              height: 15,
-              content: ' ',
-            },
-          },
-        },
-        style,
-      ])}`}
-    />
-  );
-}
diff --git a/packages/desktop-client/src/components/mobile/MobileForms.tsx b/packages/desktop-client/src/components/mobile/MobileForms.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6141192bdab16a9418a6307e619a85f3af070c36
--- /dev/null
+++ b/packages/desktop-client/src/components/mobile/MobileForms.tsx
@@ -0,0 +1,186 @@
+import React, {
+  type ComponentPropsWithoutRef,
+  forwardRef,
+  type ReactNode,
+} from 'react';
+
+import { css } from 'glamor';
+
+import { theme, styles, type CSSProperties } from '../../style';
+import { Button } from '../common/Button';
+import { Input } from '../common/Input';
+import { Text } from '../common/Text';
+import { View } from '../common/View';
+
+type FieldLabelProps = {
+  title: string;
+  flush?: boolean;
+  style?: CSSProperties;
+};
+
+export function FieldLabel({ title, flush, style }: FieldLabelProps) {
+  return (
+    <Text
+      style={{
+        marginBottom: 5,
+        marginTop: flush ? 0 : 25,
+        fontSize: 13,
+        color: theme.tableRowHeaderText,
+        padding: `0 ${styles.mobileEditingPadding}px`,
+        textTransform: 'uppercase',
+        userSelect: 'none',
+        ...style,
+      }}
+    >
+      {title}
+    </Text>
+  );
+}
+
+const valueStyle = {
+  borderWidth: 1,
+  borderColor: theme.formInputBorder,
+  marginLeft: 8,
+  marginRight: 8,
+  height: styles.mobileMinHeight,
+};
+
+type InputFieldProps = ComponentPropsWithoutRef<typeof Input>;
+
+export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
+  ({ disabled, style, onUpdate, ...props }, ref) => {
+    return (
+      <Input
+        inputRef={ref}
+        autoCorrect="false"
+        autoCapitalize="none"
+        disabled={disabled}
+        onUpdate={onUpdate}
+        style={{
+          ...valueStyle,
+          ...style,
+          color: disabled ? theme.tableTextInactive : theme.tableText,
+          backgroundColor: disabled
+            ? theme.formInputTextReadOnlySelection
+            : theme.tableBackground,
+        }}
+        {...props}
+      />
+    );
+  },
+);
+
+InputField.displayName = 'InputField';
+
+type TapFieldProps = ComponentPropsWithoutRef<typeof Button> & {
+  rightContent?: ReactNode;
+};
+
+export const TapField = forwardRef<HTMLButtonElement, TapFieldProps>(
+  (
+    {
+      value,
+      children,
+      disabled,
+      rightContent,
+      style,
+      textStyle,
+      onClick,
+      ...props
+    },
+    ref,
+  ) => {
+    return (
+      <Button
+        // @ts-expect-error fix this later
+        as={View}
+        ref={ref}
+        onClick={!disabled ? onClick : undefined}
+        style={{
+          flexDirection: 'row',
+          alignItems: 'center',
+          ...style,
+          ...valueStyle,
+          backgroundColor: theme.tableBackground,
+          ...(disabled && {
+            backgroundColor: theme.formInputTextReadOnlySelection,
+          }),
+        }}
+        bounce={false}
+        activeStyle={{
+          opacity: 0.5,
+          boxShadow: 'none',
+        }}
+        hoveredStyle={{
+          boxShadow: 'none',
+        }}
+        // activeOpacity={0.05}
+        {...props}
+      >
+        {children ? (
+          children
+        ) : (
+          <Text style={{ flex: 1, userSelect: 'none', ...textStyle }}>
+            {value}
+          </Text>
+        )}
+        {!disabled && rightContent}
+      </Button>
+    );
+  },
+);
+
+TapField.displayName = 'TapField';
+
+type BooleanFieldProps = {
+  checked: boolean;
+  disabled?: boolean;
+  onUpdate?: (checked: boolean) => void;
+  style?: CSSProperties;
+};
+
+export function BooleanField({
+  checked,
+  onUpdate,
+  style,
+  disabled = false,
+}: BooleanFieldProps) {
+  return (
+    <input
+      disabled={disabled ? true : undefined}
+      type="checkbox"
+      checked={checked}
+      onChange={e => onUpdate?.(e.target.checked)}
+      className={`${css([
+        {
+          marginInline: styles.mobileEditingPadding,
+          flexShrink: 0,
+          appearance: 'none',
+          outline: 0,
+          border: '1px solid ' + theme.formInputBorder,
+          borderRadius: 4,
+          display: 'flex',
+          alignItems: 'center',
+          justifyContent: 'center',
+          color: theme.checkboxText,
+          backgroundColor: theme.tableBackground,
+          ':checked': {
+            border: '1px solid ' + theme.checkboxBorderSelected,
+            backgroundColor: theme.checkboxBackgroundSelected,
+            '::after': {
+              display: 'block',
+              background:
+                theme.checkboxBackgroundSelected +
+                // eslint-disable-next-line rulesdir/typography
+                ' url(\'data:image/svg+xml; utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="white" d="M0 11l2-2 5 5L18 3l2 2L7 18z"/></svg>\') 15px 15px',
+              width: 15,
+              height: 15,
+              content: ' ',
+            },
+          },
+        },
+        style,
+      ])}`}
+    />
+  );
+}
diff --git a/upcoming-release-notes/2511.md b/upcoming-release-notes/2511.md
new file mode 100644
index 0000000000000000000000000000000000000000..cfcca376d5e3f96846e720fd1e344765757319af
--- /dev/null
+++ b/upcoming-release-notes/2511.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [joel-jeremy]
+---
+
+Split menu components to separate files for reusability.