diff --git a/packages/desktop-client/src/components/spreadsheet/useFormat.ts b/packages/desktop-client/src/components/spreadsheet/useFormat.ts
index f01c29a751df339e82be87f2ecde79508298f4e3..b5e13f6c5d457be768d9998aa12af0fc704488fb 100644
--- a/packages/desktop-client/src/components/spreadsheet/useFormat.ts
+++ b/packages/desktop-client/src/components/spreadsheet/useFormat.ts
@@ -1,8 +1,13 @@
-import { useCallback } from 'react';
-import { useSelector } from 'react-redux';
+import { useCallback, useEffect, useMemo } from 'react';
 
-import { selectNumberFormat } from 'loot-core/src/client/selectors';
-import { integerToCurrency } from 'loot-core/src/shared/util';
+import {
+  getNumberFormat,
+  integerToCurrency,
+  isNumberFormat,
+  setNumberFormat,
+} from 'loot-core/src/shared/util';
+
+import { useSyncedPref } from '../../hooks/useSyncedPref';
 
 export type FormatType =
   | 'string'
@@ -55,11 +60,28 @@ function format(
 }
 
 export function useFormat() {
-  const numberFormat = useSelector(selectNumberFormat);
+  const [numberFormat] = useSyncedPref('numberFormat');
+  const [hideFraction] = useSyncedPref('hideFraction');
+
+  const config = useMemo(
+    () => ({
+      format: isNumberFormat(numberFormat) ? numberFormat : 'comma-dot',
+      hideFraction: String(hideFraction) === 'true',
+    }),
+    [numberFormat, hideFraction],
+  );
+
+  // Hack: keep the global number format in sync - update the settings when
+  // the underlying configuration changes.
+  // This should be patched by moving all number-formatting utilities away from
+  // the global `getNumberFormat()` and to using the reactive `useFormat` hook.
+  useEffect(() => {
+    setNumberFormat(config);
+  }, [config]);
 
   return useCallback(
     (value: unknown, type: FormatType = 'string') =>
-      format(value, type, numberFormat.formatter),
-    [numberFormat],
+      format(value, type, getNumberFormat(config).formatter),
+    [config],
   );
 }
diff --git a/packages/desktop-client/src/hooks/useDateFormat.ts b/packages/desktop-client/src/hooks/useDateFormat.ts
index 258f156e1fc978566829a11da3f0de6ece4d549b..9a4800ca221a54d80578292eb6f0ebddc8823ceb 100644
--- a/packages/desktop-client/src/hooks/useDateFormat.ts
+++ b/packages/desktop-client/src/hooks/useDateFormat.ts
@@ -1,7 +1,6 @@
-import { useSelector } from 'react-redux';
-
-import { type State } from 'loot-core/src/client/state-types';
+import { useSyncedPref } from './useSyncedPref';
 
 export function useDateFormat() {
-  return useSelector((state: State) => state.prefs.local?.dateFormat);
+  const [dateFormat] = useSyncedPref('dateFormat');
+  return dateFormat;
 }
diff --git a/packages/desktop-client/src/hooks/useFeatureFlag.ts b/packages/desktop-client/src/hooks/useFeatureFlag.ts
index 7bfd067d09728dff73e8fb467d173daf62c0d16d..eb210ec47ca29e2efcba0616cc16b4d9a5aaa262 100644
--- a/packages/desktop-client/src/hooks/useFeatureFlag.ts
+++ b/packages/desktop-client/src/hooks/useFeatureFlag.ts
@@ -1,8 +1,7 @@
-import { useSelector } from 'react-redux';
-
-import { type State } from 'loot-core/src/client/state-types';
 import type { FeatureFlag } from 'loot-core/src/types/prefs';
 
+import { useSyncedPref } from './useSyncedPref';
+
 const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = {
   reportBudget: false,
   goalTemplatesEnabled: false,
@@ -12,11 +11,9 @@ const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = {
 };
 
 export function useFeatureFlag(name: FeatureFlag): boolean {
-  return useSelector((state: State) => {
-    const value = state.prefs.local[`flags.${name}`];
+  const [value] = useSyncedPref(`flags.${name}`);
 
-    return value === undefined
-      ? DEFAULT_FEATURE_FLAG_STATE[name] || false
-      : String(value) === 'true';
-  });
+  return value === undefined
+    ? DEFAULT_FEATURE_FLAG_STATE[name] || false
+    : String(value) === 'true';
 }
diff --git a/packages/loot-core/src/client/reducers/prefs.ts b/packages/loot-core/src/client/reducers/prefs.ts
index 0d74961596a32ab1761ade7011c8e5b1caecf101..5523f5d552c0f6256920e38b3c27ab86c29695b9 100644
--- a/packages/loot-core/src/client/reducers/prefs.ts
+++ b/packages/loot-core/src/client/reducers/prefs.ts
@@ -1,5 +1,4 @@
 // @ts-strict-ignore
-import { isNumberFormat, setNumberFormat } from '../../shared/util';
 import * as constants from '../constants';
 import type { Action } from '../state-types';
 import type { PrefsState } from '../state-types/prefs';
@@ -12,32 +11,8 @@ const initialState: PrefsState = {
 export function update(state = initialState, action: Action): PrefsState {
   switch (action.type) {
     case constants.SET_PREFS:
-      if (action.prefs) {
-        setNumberFormat({
-          format: isNumberFormat(action.prefs.numberFormat)
-            ? action.prefs.numberFormat
-            : 'comma-dot',
-          hideFraction: String(action.prefs.hideFraction) === 'true',
-        });
-      }
       return { local: action.prefs, global: action.globalPrefs };
     case constants.MERGE_LOCAL_PREFS:
-      if (action.prefs.numberFormat || action.prefs.hideFraction != null) {
-        setNumberFormat({
-          format: isNumberFormat(action.prefs.numberFormat)
-            ? action.prefs.numberFormat
-            : isNumberFormat(state.local.numberFormat)
-              ? state.local.numberFormat
-              : 'comma-dot',
-          hideFraction:
-            String(
-              action.prefs.hideFraction != null
-                ? action.prefs.hideFraction
-                : state.local.hideFraction,
-            ) === 'true',
-        });
-      }
-
       return {
         ...state,
         local: { ...state.local, ...action.prefs },
diff --git a/packages/loot-core/src/client/selectors.ts b/packages/loot-core/src/client/selectors.ts
deleted file mode 100644
index ddb4356891981dd2516c6c325e5068b91fa9a2b0..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/client/selectors.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// @ts-strict-ignore
-import { createSelector } from 'reselect';
-
-import { getNumberFormat, isNumberFormat } from '../shared/util';
-
-import type { State } from './state-types';
-
-const getState = (state: State) => state;
-
-const getPrefsState = createSelector(getState, state => state.prefs);
-const getLocalPrefsState = createSelector(getPrefsState, prefs => prefs.local);
-
-export const selectNumberFormat = createSelector(getLocalPrefsState, prefs =>
-  getNumberFormat({
-    format: isNumberFormat(prefs.numberFormat) ? prefs.numberFormat : undefined,
-    hideFraction: String(prefs.hideFraction) === 'true',
-  }),
-);
diff --git a/packages/loot-core/src/client/state-types/prefs.d.ts b/packages/loot-core/src/client/state-types/prefs.d.ts
index 275ee553229807bd1859a95a4c6c8ec52bf9aec8..7f125962f315017c9206b6e0c50ddcc0059dfa28 100644
--- a/packages/loot-core/src/client/state-types/prefs.d.ts
+++ b/packages/loot-core/src/client/state-types/prefs.d.ts
@@ -1,25 +1,20 @@
-import type {
-  GlobalPrefs,
-  LocalPrefs,
-  MetadataPrefs,
-  SyncedPrefs,
-} from '../../types/prefs';
+import type { GlobalPrefs, LocalPrefs, MetadataPrefs } from '../../types/prefs';
 import type * as constants from '../constants';
 
 export type PrefsState = {
-  local: LocalPrefs & MetadataPrefs & SyncedPrefs;
+  local: LocalPrefs & MetadataPrefs;
   global: GlobalPrefs;
 };
 
 export type SetPrefsAction = {
   type: typeof constants.SET_PREFS;
-  prefs: LocalPrefs & MetadataPrefs & SyncedPrefs;
+  prefs: LocalPrefs & MetadataPrefs;
   globalPrefs: GlobalPrefs;
 };
 
 export type MergeLocalPrefsAction = {
   type: typeof constants.MERGE_LOCAL_PREFS;
-  prefs: LocalPrefs & MetadataPrefs & SyncedPrefs;
+  prefs: LocalPrefs & MetadataPrefs;
 };
 
 export type MergeGlobalPrefsAction = {
diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts
index 879a8535fc0f46bd06a102741d77aa428cc81dad..43dabd75372229bb3de1dea07e2fd27b845f363e 100644
--- a/packages/loot-core/src/shared/util.ts
+++ b/packages/loot-core/src/shared/util.ts
@@ -233,7 +233,7 @@ const NUMBER_FORMATS = [
 
 type NumberFormats = (typeof NUMBER_FORMATS)[number];
 
-export function isNumberFormat(input: string): input is NumberFormats {
+export function isNumberFormat(input: string = ''): input is NumberFormats {
   return (NUMBER_FORMATS as readonly string[]).includes(input);
 }
 
diff --git a/upcoming-release-notes/3397.md b/upcoming-release-notes/3397.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb5ceb11f2ed2be99d65587d287735d8e566379e
--- /dev/null
+++ b/upcoming-release-notes/3397.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+SyncedPrefs: refactor usages of number formatter away from redux.