diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png
index 58229d4ae3e4a0aa362e4882381a67302bf34f95..18956272b5608ab7d263cf0da4fa4160f6f7d6b0 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png
index 278d72136022b255b899859161b5f9763f4d3731..897bc6b1af0963e85a141a95d6c3a5ee96415593 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-2-chromium-linux.png differ
diff --git a/packages/desktop-client/src/components/budget/BudgetCategories.jsx b/packages/desktop-client/src/components/budget/BudgetCategories.jsx
index 962697d9a9c05101857713df4eef160de3273470..d6a1a52f79678115223fbdecc028df869b2554c7 100644
--- a/packages/desktop-client/src/components/budget/BudgetCategories.jsx
+++ b/packages/desktop-client/src/components/budget/BudgetCategories.jsx
@@ -31,11 +31,11 @@ export const BudgetCategories = memo(
     onReorderCategory,
     onReorderGroup,
   }) => {
-    const [_collapsed, setCollapsedPref] = useLocalPref('budget.collapsed');
-    const collapsed = _collapsed || [];
+    const [collapsedGroupIds = [], setCollapsedGroupIdsPref] =
+      useLocalPref('budget.collapsed');
     const [showHiddenCategories] = useLocalPref('budget.showHiddenCategories');
     function onCollapse(value) {
-      setCollapsedPref(value);
+      setCollapsedGroupIdsPref(value);
     }
 
     const [isAddingGroup, setIsAddingGroup] = useState(false);
@@ -62,13 +62,14 @@ export const BudgetCategories = memo(
 
           return [
             ...items,
-            ...(collapsed.includes(group.id) ? [] : groupCategories).map(
-              cat => ({
-                type: 'expense-category',
-                value: cat,
-                group,
-              }),
-            ),
+            ...(collapsedGroupIds.includes(group.id)
+              ? []
+              : groupCategories
+            ).map(cat => ({
+              type: 'expense-category',
+              value: cat,
+              group,
+            })),
           ];
         }),
       );
@@ -83,7 +84,7 @@ export const BudgetCategories = memo(
             { type: 'income-separator' },
             { type: 'income-group', value: incomeGroup },
             newCategoryForGroup === incomeGroup.id && { type: 'new-category' },
-            ...(collapsed.includes(incomeGroup.id)
+            ...(collapsedGroupIds.includes(incomeGroup.id)
               ? []
               : incomeGroup.categories.filter(
                   cat => showHiddenCategories || !cat.hidden,
@@ -99,7 +100,7 @@ export const BudgetCategories = memo(
       return items;
     }, [
       categoryGroups,
-      collapsed,
+      collapsedGroupIds,
       newCategoryForGroup,
       isAddingGroup,
       showHiddenCategories,
@@ -125,7 +126,7 @@ export const BudgetCategories = memo(
             ...dragState,
             preview: false,
           });
-          setSavedCollapsed(collapsed);
+          setSavedCollapsed(collapsedGroupIds);
         }
       } else if (state === 'hover') {
         setDragState({
@@ -140,10 +141,10 @@ export const BudgetCategories = memo(
     }
 
     function onToggleCollapse(id) {
-      if (collapsed.includes(id)) {
-        onCollapse(collapsed.filter(id_ => id_ !== id));
+      if (collapsedGroupIds.includes(id)) {
+        onCollapse(collapsedGroupIds.filter(id_ => id_ !== id));
       } else {
-        onCollapse([...collapsed, id]);
+        onCollapse([...collapsedGroupIds, id]);
       }
     }
 
@@ -163,7 +164,7 @@ export const BudgetCategories = memo(
     }
 
     function onShowNewCategory(groupId) {
-      onCollapse(collapsed.filter(c => c !== groupId));
+      onCollapse(collapsedGroupIds.filter(c => c !== groupId));
       setNewCategoryForGroup(groupId);
     }
 
@@ -233,7 +234,7 @@ export const BudgetCategories = memo(
                 <ExpenseGroup
                   group={item.value}
                   editingCell={editingCell}
-                  collapsed={collapsed.includes(item.value.id)}
+                  collapsed={collapsedGroupIds.includes(item.value.id)}
                   MonthComponent={dataComponents.ExpenseGroupComponent}
                   dragState={dragState}
                   onEditName={onEditName}
@@ -287,7 +288,7 @@ export const BudgetCategories = memo(
                   group={item.value}
                   editingCell={editingCell}
                   MonthComponent={dataComponents.IncomeGroupComponent}
-                  collapsed={collapsed.includes(item.value.id)}
+                  collapsed={collapsedGroupIds.includes(item.value.id)}
                   onEditName={onEditName}
                   onSave={_onSaveGroup}
                   onToggleCollapse={onToggleCollapse}
diff --git a/packages/desktop-client/src/components/budget/BudgetTable.jsx b/packages/desktop-client/src/components/budget/BudgetTable.jsx
index f4d45208dce1279046207f58526fcce41911d98d..c9bd7acecbee1a1e9a0524b070edcde0f3862889 100644
--- a/packages/desktop-client/src/components/budget/BudgetTable.jsx
+++ b/packages/desktop-client/src/components/budget/BudgetTable.jsx
@@ -32,7 +32,8 @@ export function BudgetTable(props) {
 
   const budgetCategoriesRef = useRef();
   const { grouped: categoryGroups } = useCategories();
-  const [collapsed = [], setCollapsedPref] = useLocalPref('budget.collapsed');
+  const [collapsedGroupIds = [], setCollapsedGroupIdsPref] =
+    useLocalPref('budget.collapsed');
   const [showHiddenCategories, setShowHiddenCategoriesPef] = useLocalPref(
     'budget.showHiddenCategories',
   );
@@ -95,7 +96,7 @@ export function BudgetTable(props) {
 
   const moveVertically = dir => {
     const flattened = categoryGroups.reduce((all, group) => {
-      if (collapsed.includes(group.id)) {
+      if (collapsedGroupIds.includes(group.id)) {
         return all.concat({ id: group.id, isGroup: true });
       }
       return all.concat([{ id: group.id, isGroup: true }, ...group.categories]);
@@ -133,7 +134,7 @@ export function BudgetTable(props) {
   };
 
   const onCollapse = collapsedIds => {
-    setCollapsedPref(collapsedIds);
+    setCollapsedGroupIdsPref(collapsedIds);
   };
 
   const onToggleHiddenCategories = () => {
diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
index c3db205c32ad1bf35989fba477d443dbf3c7239f..952c8f90fd708f8b8223e5c47c1a9b7b801c75e5 100644
--- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
+++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx
@@ -10,7 +10,13 @@ import * as monthUtils from 'loot-core/src/shared/months';
 import { useLocalPref } from '../../../hooks/useLocalPref';
 import { useNavigate } from '../../../hooks/useNavigate';
 import { SvgLogo } from '../../../icons/logo';
-import { SvgAdd, SvgArrowThinLeft, SvgArrowThinRight } from '../../../icons/v1';
+import {
+  SvgAdd,
+  SvgArrowThinLeft,
+  SvgArrowThinRight,
+  SvgCheveronDown,
+  SvgCheveronRight,
+} from '../../../icons/v1';
 import { useResponsive } from '../../../ResponsiveProvider';
 import { theme, styles } from '../../../style';
 import { BalanceWithCarryover } from '../../budget/BalanceWithCarryover';
@@ -192,7 +198,7 @@ function ExpenseGroupPreview({ group, pending, style }) {
         opacity: pending ? 1 : 0.4,
       }}
     >
-      <ExpenseGroupTotals group={group} blank={true} />
+      <ExpenseGroupHeader group={group} blank={true} />
 
       {group.categories.map((cat, index) => (
         <ExpenseCategory
@@ -445,7 +451,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
   // </Draggable>
 });
 
-const ExpenseGroupTotals = memo(function ExpenseGroupTotals({
+const ExpenseGroupHeader = memo(function ExpenseGroupHeader({
   group,
   budgeted,
   spent,
@@ -455,6 +461,8 @@ const ExpenseGroupTotals = memo(function ExpenseGroupTotals({
   blank,
   show3Cols,
   showBudgetedCol,
+  collapsed,
+  onToggleCollapse,
 }) {
   const opacity = blank ? 0 : 1;
   const listItemRef = useRef();
@@ -466,11 +474,36 @@ const ExpenseGroupTotals = memo(function ExpenseGroupTotals({
         alignItems: 'center',
         backgroundColor: theme.tableRowHeaderBackground,
         opacity: !!group.hidden ? 0.5 : undefined,
+        paddingLeft: 0,
       }}
       data-testid="totals"
       innerRef={listItemRef}
     >
-      <View role="button" style={{ flex: 1 }}>
+      <View
+        role="button"
+        style={{
+          flex: 1,
+          alignItems: 'center',
+          flexDirection: 'row',
+        }}
+      >
+        <Button
+          type="bare"
+          style={{ margin: '0 1px', ...styles.noTapHighlight }}
+          activeStyle={{
+            backgroundColor: 'transparent',
+          }}
+          hoveredStyle={{
+            backgroundColor: 'transparent',
+          }}
+          onClick={() => onToggleCollapse?.(group.id)}
+        >
+          {collapsed ? (
+            <SvgCheveronRight width={14} height={14} />
+          ) : (
+            <SvgCheveronDown width={14} height={14} />
+          )}
+        </Button>
         <Text
           style={{
             ...styles.smallText,
@@ -582,12 +615,13 @@ const ExpenseGroupTotals = memo(function ExpenseGroupTotals({
   // </Droppable>
 });
 
-const IncomeGroupTotals = memo(function IncomeGroupTotals({
+const IncomeGroupHeader = memo(function IncomeGroupHeader({
   group,
   budgeted,
   balance,
-  style,
   onEdit,
+  collapsed,
+  onToggleCollapse,
 }) {
   const listItemRef = useRef();
 
@@ -596,10 +630,9 @@ const IncomeGroupTotals = memo(function IncomeGroupTotals({
       style={{
         flexDirection: 'row',
         alignItems: 'center',
-        padding: 10,
         backgroundColor: theme.tableRowHeaderBackground,
         opacity: !!group.hidden ? 0.5 : undefined,
-        ...style,
+        paddingLeft: 0,
       }}
       innerRef={listItemRef}
     >
@@ -607,11 +640,28 @@ const IncomeGroupTotals = memo(function IncomeGroupTotals({
         role="button"
         style={{
           flex: 1,
-          justifyContent: 'center',
-          alignItems: 'flex-start',
+          alignItems: 'center',
+          flexDirection: 'row',
           height: ROW_HEIGHT,
         }}
       >
+        <Button
+          type="bare"
+          style={{ margin: '0 1px', ...styles.noTapHighlight }}
+          activeStyle={{
+            backgroundColor: 'transparent',
+          }}
+          hoveredStyle={{
+            backgroundColor: 'transparent',
+          }}
+          onClick={() => onToggleCollapse?.(group.id)}
+        >
+          {collapsed ? (
+            <SvgCheveronRight width={14} height={14} />
+          ) : (
+            <SvgCheveronDown width={14} height={14} />
+          )}
+        </Button>
         <Text
           style={{
             ...styles.smallText,
@@ -684,7 +734,6 @@ const IncomeCategory = memo(function IncomeCategory({
       style={{
         flexDirection: 'row',
         alignItems: 'center',
-        padding: 10,
         backgroundColor: 'transparent',
         borderBottomWidth: 0,
         borderTopWidth: index > 0 ? 1 : 0,
@@ -799,6 +848,8 @@ const ExpenseGroup = memo(function ExpenseGroup({
   showBudgetedCol,
   show3Cols,
   showHiddenCategories,
+  collapsed,
+  onToggleCollapse,
 }) {
   function editable(content) {
     if (!editMode) {
@@ -832,11 +883,11 @@ const ExpenseGroup = memo(function ExpenseGroup({
   return editable(
     <Card
       style={{
-        marginTop: 7,
-        marginBottom: 7,
+        marginTop: 4,
+        marginBottom: 4,
       }}
     >
-      <ExpenseGroupTotals
+      <ExpenseGroupHeader
         group={group}
         showBudgetedCol={showBudgetedCol}
         budgeted={
@@ -858,11 +909,15 @@ const ExpenseGroup = memo(function ExpenseGroup({
         editMode={editMode}
         onAddCategory={onAddCategory}
         onEdit={onEditGroup}
+        collapsed={collapsed}
+        onToggleCollapse={onToggleCollapse}
         // onReorderCategory={onReorderCategory}
       />
 
       {group.categories
-        .filter(category => !category.hidden || showHiddenCategories)
+        .filter(
+          category => !collapsed && (!category.hidden || showHiddenCategories),
+        )
         .map((category, index) => {
           return (
             <ExpenseCategory
@@ -924,6 +979,8 @@ function IncomeGroup({
   onEditGroup,
   onEditCategory,
   onBudgetAction,
+  collapsed,
+  onToggleCollapse,
 }) {
   return (
     <View>
@@ -942,7 +999,7 @@ function IncomeGroup({
       </View>
 
       <Card style={{ marginTop: 0 }}>
-        <IncomeGroupTotals
+        <IncomeGroupHeader
           group={group}
           budgeted={
             type === 'report' ? reportBudget.groupBudgeted(group.id) : null
@@ -952,16 +1009,18 @@ function IncomeGroup({
               ? reportBudget.groupSumAmount(group.id)
               : rolloverBudget.groupSumAmount(group.id)
           }
-          style={{
-            backgroundColor: theme.tableRowHeaderBackground,
-          }}
           onAddCategory={onAddCategory}
           editMode={editMode}
           onEdit={onEditGroup}
+          collapsed={collapsed}
+          onToggleCollapse={onToggleCollapse}
         />
 
         {group.categories
-          .filter(category => !category.hidden || showHiddenCategories)
+          .filter(
+            category =>
+              !collapsed && (!category.hidden || showHiddenCategories),
+          )
           .map((category, index) => {
             return (
               <IncomeCategory
@@ -1020,6 +1079,16 @@ function BudgetGroups({
   });
 
   const { incomeGroup, expenseGroups } = separateGroups(categoryGroups);
+  const [collapsedGroupIds = [], setCollapsedGroupIdsPref] =
+    useLocalPref('budget.collapsed');
+
+  const onToggleCollapse = id => {
+    setCollapsedGroupIdsPref(
+      collapsedGroupIds.includes(id)
+        ? collapsedGroupIds.filter(collapsedId => collapsedId !== id)
+        : [...collapsedGroupIds, id],
+    );
+  };
 
   return (
     <View
@@ -1048,6 +1117,8 @@ function BudgetGroups({
               onBudgetAction={onBudgetAction}
               show3Cols={show3Cols}
               showHiddenCategories={showHiddenCategories}
+              collapsed={collapsedGroupIds.includes(group.id)}
+              onToggleCollapse={onToggleCollapse}
             />
           );
         })}
@@ -1065,6 +1136,8 @@ function BudgetGroups({
           onEditGroup={onEditGroup}
           onEditCategory={onEditCategory}
           onBudgetAction={onBudgetAction}
+          collapsed={collapsedGroupIds.includes(incomeGroup.id)}
+          onToggleCollapse={onToggleCollapse}
         />
       )}
     </View>
diff --git a/upcoming-release-notes/2611.md b/upcoming-release-notes/2611.md
new file mode 100644
index 0000000000000000000000000000000000000000..979408f50c8effffbe1b45e2e0986f622e4901b9
--- /dev/null
+++ b/upcoming-release-notes/2611.md
@@ -0,0 +1,6 @@
+---
+category: Features
+authors: [joel-jeremy]
+---
+
+Collapsible budget groups in mobile.