diff --git a/packages/desktop-client/src/components/reports/Overview.tsx b/packages/desktop-client/src/components/reports/Overview.tsx
index 0a0d5f7455d5df48a5b5ce8f9dfe49640f9c21fb..0587f6dc7d0bbb90fadd812f0dc6d72e83b56721 100644
--- a/packages/desktop-client/src/components/reports/Overview.tsx
+++ b/packages/desktop-client/src/components/reports/Overview.tsx
@@ -46,10 +46,9 @@ function isCustomReportWidget(widget: Widget): widget is CustomReportWidget {
   return widget.type === 'custom-report';
 }
 
-function useWidgetLayout(widgets: Widget[]): (Layout & {
-  type: Widget['type'];
-  meta: Widget['meta'];
-})[] {
+type LayoutWidget = Layout & Pick<Widget, 'type' | 'meta'>;
+
+function useWidgetLayout(widgets: Widget[]): LayoutWidget[] {
   return widgets.map(widget => ({
     i: widget.id,
     type: widget.type,
@@ -290,6 +289,16 @@ export function Overview() {
     );
   };
 
+  const onMetaChange = <T extends LayoutWidget>(
+    widget: T,
+    newMeta: T['meta'],
+  ) => {
+    send('dashboard-update-widget', {
+      id: widget.i,
+      meta: newMeta,
+    });
+  };
+
   const accounts = useAccounts();
 
   if (isLoading) {
@@ -494,16 +503,22 @@ export function Overview() {
                   <NetWorthCard
                     isEditing={isEditing}
                     accounts={accounts}
+                    meta={item.meta && 'name' in item.meta ? item.meta : {}}
+                    onMetaChange={newMeta => onMetaChange(item, newMeta)}
                     onRemove={() => onRemoveWidget(item.i)}
                   />
                 ) : item.type === 'cash-flow-card' ? (
                   <CashFlowCard
                     isEditing={isEditing}
+                    meta={item.meta && 'name' in item.meta ? item.meta : {}}
+                    onMetaChange={newMeta => onMetaChange(item, newMeta)}
                     onRemove={() => onRemoveWidget(item.i)}
                   />
                 ) : item.type === 'spending-card' ? (
                   <SpendingCard
                     isEditing={isEditing}
+                    meta={item.meta && 'name' in item.meta ? item.meta : {}}
+                    onMetaChange={newMeta => onMetaChange(item, newMeta)}
                     onRemove={() => onRemoveWidget(item.i)}
                   />
                 ) : item.type === 'custom-report' ? (
diff --git a/packages/desktop-client/src/components/reports/ReportCardName.tsx b/packages/desktop-client/src/components/reports/ReportCardName.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3eac1ece293f46a8afbc55083793be8a9d8e7504
--- /dev/null
+++ b/packages/desktop-client/src/components/reports/ReportCardName.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+
+import { styles } from '../../style';
+import { Block } from '../common/Block';
+import { InitialFocus } from '../common/InitialFocus';
+import { Input } from '../common/Input';
+
+import { NON_DRAGGABLE_AREA_CLASS_NAME } from './constants';
+
+type ReportCardNameProps = {
+  name: string;
+  isEditing: boolean;
+  onChange: (newName: string) => void;
+  onClose: () => void;
+};
+
+export const ReportCardName = ({
+  name,
+  isEditing,
+  onChange,
+  onClose,
+}: ReportCardNameProps) => {
+  if (isEditing) {
+    return (
+      <InitialFocus>
+        <Input
+          className={NON_DRAGGABLE_AREA_CLASS_NAME}
+          defaultValue={name}
+          onEnter={e => onChange(e.currentTarget.value)}
+          onUpdate={onChange}
+          onEscape={onClose}
+          style={{
+            fontSize: 15,
+            fontWeight: 500,
+            marginTop: -6,
+            marginBottom: -1,
+            marginLeft: -6,
+            width: Math.max(20, name.length) + 'ch',
+          }}
+        />
+      </InitialFocus>
+    );
+  }
+
+  return (
+    <Block
+      style={{
+        ...styles.mediumText,
+        fontWeight: 500,
+        marginBottom: 5,
+      }}
+      role="heading"
+    >
+      {name}
+    </Block>
+  );
+};
diff --git a/packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx b/packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx
index e1aa13f1a9162235071184002e5bfe977af75055..3bf8fe415ea9f3ad1ab33cdba3881040500f8a33 100644
--- a/packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx
+++ b/packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx
@@ -5,9 +5,9 @@ import { Bar, BarChart, LabelList, ResponsiveContainer } from 'recharts';
 
 import * as monthUtils from 'loot-core/src/shared/months';
 import { integerToCurrency } from 'loot-core/src/shared/util';
+import { type CashFlowWidget } from 'loot-core/src/types/models';
 
-import { theme, styles } from '../../../style';
-import { Block } from '../../common/Block';
+import { theme } from '../../../style';
 import { View } from '../../common/View';
 import { PrivacyFilter } from '../../PrivacyFilter';
 import { Change } from '../Change';
@@ -16,6 +16,7 @@ import { Container } from '../Container';
 import { DateRange } from '../DateRange';
 import { LoadingIndicator } from '../LoadingIndicator';
 import { ReportCard } from '../ReportCard';
+import { ReportCardName } from '../ReportCardName';
 import { simpleCashFlow } from '../spreadsheets/cash-flow-spreadsheet';
 import { useReport } from '../useReport';
 
@@ -81,14 +82,23 @@ function CustomLabel({
 
 type CashFlowCardProps = {
   isEditing?: boolean;
+  meta?: CashFlowWidget['meta'];
+  onMetaChange: (newMeta: CashFlowWidget['meta']) => void;
   onRemove: () => void;
 };
 
-export function CashFlowCard({ isEditing, onRemove }: CashFlowCardProps) {
+export function CashFlowCard({
+  isEditing,
+  meta,
+  onMetaChange,
+  onRemove,
+}: CashFlowCardProps) {
   const { t } = useTranslation();
   const end = monthUtils.currentDay();
   const start = monthUtils.currentMonth() + '-01';
 
+  const [nameMenuOpen, setNameMenuOpen] = useState(false);
+
   const params = useMemo(() => simpleCashFlow(start, end), [start, end]);
   const data = useReport('cash_flow_simple', params);
 
@@ -105,6 +115,10 @@ export function CashFlowCard({ isEditing, onRemove }: CashFlowCardProps) {
       isEditing={isEditing}
       to="/reports/cash-flow"
       menuItems={[
+        {
+          name: 'rename',
+          text: t('Rename'),
+        },
         {
           name: 'remove',
           text: t('Remove'),
@@ -112,6 +126,9 @@ export function CashFlowCard({ isEditing, onRemove }: CashFlowCardProps) {
       ]}
       onMenuSelect={item => {
         switch (item) {
+          case 'rename':
+            setNameMenuOpen(true);
+            break;
           case 'remove':
             onRemove();
             break;
@@ -127,12 +144,18 @@ export function CashFlowCard({ isEditing, onRemove }: CashFlowCardProps) {
       >
         <View style={{ flexDirection: 'row', padding: 20 }}>
           <View style={{ flex: 1 }}>
-            <Block
-              style={{ ...styles.mediumText, fontWeight: 500, marginBottom: 5 }}
-              role="heading"
-            >
-              Cash Flow
-            </Block>
+            <ReportCardName
+              name={meta?.name || t('Cash Flow')}
+              isEditing={nameMenuOpen}
+              onChange={newName => {
+                onMetaChange({
+                  ...meta,
+                  name: newName,
+                });
+                setNameMenuOpen(false);
+              }}
+              onClose={() => setNameMenuOpen(false)}
+            />
             <DateRange start={start} end={end} />
           </View>
           {data && (
diff --git a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx
index d654d17c90e840ca47c2a6654a256f150e164ede..a083b991dc1eef7a1c3421f4707f14972db998c7 100644
--- a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx
+++ b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx
@@ -14,15 +14,12 @@ import { useSyncedPref } from '../../../hooks/useSyncedPref';
 import { SvgExclamationSolid } from '../../../icons/v1';
 import { styles } from '../../../style/index';
 import { theme } from '../../../style/theme';
-import { Block } from '../../common/Block';
-import { InitialFocus } from '../../common/InitialFocus';
-import { Input } from '../../common/Input';
 import { Text } from '../../common/Text';
 import { Tooltip } from '../../common/Tooltip';
 import { View } from '../../common/View';
-import { NON_DRAGGABLE_AREA_CLASS_NAME } from '../constants';
 import { DateRange } from '../DateRange';
 import { ReportCard } from '../ReportCard';
+import { ReportCardName } from '../ReportCardName';
 
 import { GetCardData } from './GetCardData';
 import { MissingReportCard } from './MissingReportCard';
@@ -146,38 +143,12 @@ function CustomReportListCardsInner({
           }}
         >
           <View style={{ flex: 1 }}>
-            {nameMenuOpen ? (
-              <InitialFocus>
-                <Input
-                  className={NON_DRAGGABLE_AREA_CLASS_NAME}
-                  defaultValue={report.name}
-                  onEnter={e =>
-                    onSaveName((e.target as HTMLInputElement).value)
-                  }
-                  onBlur={e => onSaveName(e.target.value)}
-                  onEscape={() => setNameMenuOpen(false)}
-                  style={{
-                    fontSize: 15,
-                    fontWeight: 500,
-                    marginTop: -6,
-                    marginBottom: -1,
-                    marginLeft: -6,
-                    width: Math.max(20, report.name.length) + 'ch',
-                  }}
-                />
-              </InitialFocus>
-            ) : (
-              <Block
-                style={{
-                  ...styles.mediumText,
-                  fontWeight: 500,
-                  marginBottom: 5,
-                }}
-                role="heading"
-              >
-                {report.name}
-              </Block>
-            )}
+            <ReportCardName
+              name={report.name}
+              isEditing={nameMenuOpen}
+              onChange={onSaveName}
+              onClose={() => setNameMenuOpen(false)}
+            />
             {report.isDateStatic ? (
               <DateRange start={report.startDate} end={report.endDate} />
             ) : (
diff --git a/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx
index 877312728e7053d2eb4d59e6f752db1dafe2e91c..5ba5ff082e1fcc1ebfa7982cf94df80a3358b3f8 100644
--- a/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx
+++ b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx
@@ -3,7 +3,10 @@ import { useTranslation } from 'react-i18next';
 
 import * as monthUtils from 'loot-core/src/shared/months';
 import { integerToCurrency } from 'loot-core/src/shared/util';
-import { type AccountEntity } from 'loot-core/src/types/models';
+import {
+  type AccountEntity,
+  type NetWorthWidget,
+} from 'loot-core/src/types/models';
 
 import { useResponsive } from '../../../ResponsiveProvider';
 import { styles } from '../../../style';
@@ -15,23 +18,30 @@ import { DateRange } from '../DateRange';
 import { NetWorthGraph } from '../graphs/NetWorthGraph';
 import { LoadingIndicator } from '../LoadingIndicator';
 import { ReportCard } from '../ReportCard';
+import { ReportCardName } from '../ReportCardName';
 import { createSpreadsheet as netWorthSpreadsheet } from '../spreadsheets/net-worth-spreadsheet';
 import { useReport } from '../useReport';
 
 type NetWorthCardProps = {
   isEditing?: boolean;
   accounts: AccountEntity[];
+  meta?: NetWorthWidget['meta'];
+  onMetaChange: (newMeta: NetWorthWidget['meta']) => void;
   onRemove: () => void;
 };
 
 export function NetWorthCard({
   isEditing,
   accounts,
+  meta = {},
+  onMetaChange,
   onRemove,
 }: NetWorthCardProps) {
   const { t } = useTranslation();
   const { isNarrowWidth } = useResponsive();
 
+  const [nameMenuOpen, setNameMenuOpen] = useState(false);
+
   const end = monthUtils.currentMonth();
   const start = monthUtils.subMonths(end, 5);
   const [isCardHovered, setIsCardHovered] = useState(false);
@@ -49,6 +59,10 @@ export function NetWorthCard({
       isEditing={isEditing}
       to="/reports/net-worth"
       menuItems={[
+        {
+          name: 'rename',
+          text: t('Rename'),
+        },
         {
           name: 'remove',
           text: t('Remove'),
@@ -56,6 +70,9 @@ export function NetWorthCard({
       ]}
       onMenuSelect={item => {
         switch (item) {
+          case 'rename':
+            setNameMenuOpen(true);
+            break;
           case 'remove':
             onRemove();
             break;
@@ -71,12 +88,18 @@ export function NetWorthCard({
       >
         <View style={{ flexDirection: 'row', padding: 20 }}>
           <View style={{ flex: 1 }}>
-            <Block
-              style={{ ...styles.mediumText, fontWeight: 500, marginBottom: 5 }}
-              role="heading"
-            >
-              Net Worth
-            </Block>
+            <ReportCardName
+              name={meta?.name || t('Net Worth')}
+              isEditing={nameMenuOpen}
+              onChange={newName => {
+                onMetaChange({
+                  ...meta,
+                  name: newName,
+                });
+                setNameMenuOpen(false);
+              }}
+              onClose={() => setNameMenuOpen(false)}
+            />
             <DateRange start={start} end={end} />
           </View>
           {data && (
diff --git a/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx b/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx
index 23c1e8a33fb37b28047f83fed8157c9bcb4728c3..3e99ac1edf93f3de571e825bd3b792a798945a22 100644
--- a/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx
+++ b/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx
@@ -3,6 +3,7 @@ import { Trans, useTranslation } from 'react-i18next';
 
 import * as monthUtils from 'loot-core/src/shared/months';
 import { amountToCurrency } from 'loot-core/src/shared/util';
+import { type SpendingWidget } from 'loot-core/src/types/models';
 
 import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
 import { useLocalPref } from '../../../hooks/useLocalPref';
@@ -15,6 +16,7 @@ import { DateRange } from '../DateRange';
 import { SpendingGraph } from '../graphs/SpendingGraph';
 import { LoadingIndicator } from '../LoadingIndicator';
 import { ReportCard } from '../ReportCard';
+import { ReportCardName } from '../ReportCardName';
 import { createSpendingSpreadsheet } from '../spreadsheets/spending-spreadsheet';
 import { useReport } from '../useReport';
 
@@ -22,10 +24,17 @@ import { MissingReportCard } from './MissingReportCard';
 
 type SpendingCardProps = {
   isEditing?: boolean;
+  meta?: SpendingWidget['meta'];
+  onMetaChange: (newMeta: SpendingWidget['meta']) => void;
   onRemove: () => void;
 };
 
-export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) {
+export function SpendingCard({
+  isEditing,
+  meta,
+  onMetaChange,
+  onRemove,
+}: SpendingCardProps) {
   const { t } = useTranslation();
 
   const [isCardHovered, setIsCardHovered] = useState(false);
@@ -35,6 +44,8 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) {
     'spendingReportCompare',
   );
 
+  const [nameMenuOpen, setNameMenuOpen] = useState(false);
+
   const parseFilter = spendingReportFilter && JSON.parse(spendingReportFilter);
   const getGraphData = useMemo(() => {
     return createSpendingSpreadsheet({
@@ -73,6 +84,10 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) {
       isEditing={isEditing}
       to="/reports/spending"
       menuItems={[
+        {
+          name: 'rename',
+          text: t('Rename'),
+        },
         {
           name: 'remove',
           text: t('Remove'),
@@ -80,6 +95,9 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) {
       ]}
       onMenuSelect={item => {
         switch (item) {
+          case 'rename':
+            setNameMenuOpen(true);
+            break;
           case 'remove':
             onRemove();
             break;
@@ -95,12 +113,18 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) {
       >
         <View style={{ flexDirection: 'row', padding: 20 }}>
           <View style={{ flex: 1 }}>
-            <Block
-              style={{ ...styles.mediumText, fontWeight: 500, marginBottom: 5 }}
-              role="heading"
-            >
-              Monthly Spending
-            </Block>
+            <ReportCardName
+              name={meta?.name || t('Monthly Spending')}
+              isEditing={nameMenuOpen}
+              onChange={newName => {
+                onMetaChange({
+                  ...meta,
+                  name: newName,
+                });
+                setNameMenuOpen(false);
+              }}
+              onClose={() => setNameMenuOpen(false)}
+            />
             <DateRange
               start={monthUtils.addMonths(monthUtils.currentMonth(), 1)}
               end={monthUtils.addMonths(monthUtils.currentMonth(), 1)}
diff --git a/packages/loot-core/src/server/dashboard/app.ts b/packages/loot-core/src/server/dashboard/app.ts
index db6267d10655526970c87c663b5ff8fee8bac4fb..edb825eee7ba180f49e700be6f99cb30b84fc91b 100644
--- a/packages/loot-core/src/server/dashboard/app.ts
+++ b/packages/loot-core/src/server/dashboard/app.ts
@@ -117,7 +117,7 @@ async function updateDashboard(
 async function updateDashboardWidget(
   widget: EverythingButIdOptional<Omit<Widget, 'tombstone'>>,
 ) {
-  await db.update('dashboard', widget);
+  await db.updateWithSchema('dashboard', widget);
 }
 
 async function resetDashboard() {
diff --git a/packages/loot-core/src/types/models/dashboard.d.ts b/packages/loot-core/src/types/models/dashboard.d.ts
index 87e809a127a8e7f40f12ef6944d0686aba095aff..2d0547326715511895e6f1d1444ec6d8a3747cf0 100644
--- a/packages/loot-core/src/types/models/dashboard.d.ts
+++ b/packages/loot-core/src/types/models/dashboard.d.ts
@@ -14,9 +14,18 @@ type AbstractWidget<
   tombstone: boolean;
 };
 
-type NetWorthWidget = AbstractWidget<'net-worth-card'>;
-type CashFlowWidget = AbstractWidget<'cash-flow-card'>;
-type SpendingWidget = AbstractWidget<'spending-card'>;
+export type NetWorthWidget = AbstractWidget<
+  'net-worth-card',
+  { name?: string } | null
+>;
+export type CashFlowWidget = AbstractWidget<
+  'cash-flow-card',
+  { name?: string } | null
+>;
+export type SpendingWidget = AbstractWidget<
+  'spending-card',
+  { name?: string } | null
+>;
 export type CustomReportWidget = AbstractWidget<
   'custom-report',
   { id: string }
diff --git a/upcoming-release-notes/3284.md b/upcoming-release-notes/3284.md
new file mode 100644
index 0000000000000000000000000000000000000000..c6738ce3165acf5377215448393bea8565693b0d
--- /dev/null
+++ b/upcoming-release-notes/3284.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [Matissjanis]
+---
+
+Dashboards: ability to rename all the widgets.