diff --git a/web/src/hooks.server.ts b/web/src/hooks.server.ts
index 0ac1285179..e9e5e1f149 100644
--- a/web/src/hooks.server.ts
+++ b/web/src/hooks.server.ts
@@ -1,7 +1,6 @@
import overpass from '$lib/assets/fonts/overpass/Overpass.ttf?url';
import overpassMono from '$lib/assets/fonts/overpass/OverpassMono.ttf?url';
import fouc from '$lib/utils/app?raw';
-import theme from '$lib/utils/theme?raw';
import type { Handle } from '@sveltejs/kit';
import { ModuleKind, transpileModule } from 'typescript';
@@ -16,9 +15,7 @@ const transpileFile = (content: string) => {
export const handle = (async ({ event, resolve }) => {
return resolve(event, {
transformPageChunk: ({ html }) => {
- const themePrepared = theme.replaceAll(/^export\s+/gm, '');
- const foucPrepared = fouc.replaceAll(/^import.*$/gm, themePrepared);
- const scriptFouc = ``;
+ const scriptFouc = ``;
return html
.replace('%app.font%', overpass)
diff --git a/web/src/lib/components/onboarding-page/onboarding-theme.svelte b/web/src/lib/components/onboarding-page/onboarding-theme.svelte
index f16f9b8be2..975dbd1ec3 100644
--- a/web/src/lib/components/onboarding-page/onboarding-theme.svelte
+++ b/web/src/lib/components/onboarding-page/onboarding-theme.svelte
@@ -5,7 +5,7 @@
import OnboardingCard from './onboarding-card.svelte';
import { colorTheme } from '$lib/stores/preferences.store';
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
- import { Theme } from '$lib/utils/theme';
+ import { Theme } from '$lib/constants';
import { t } from 'svelte-i18n';
export let onDone: () => void;
diff --git a/web/src/lib/components/shared-components/immich-logo.svelte b/web/src/lib/components/shared-components/immich-logo.svelte
index cb77f40ca0..34f4b9707c 100644
--- a/web/src/lib/components/shared-components/immich-logo.svelte
+++ b/web/src/lib/components/shared-components/immich-logo.svelte
@@ -3,11 +3,11 @@
import logoLightUrl from '$lib/assets/immich-logo-inline-light.svg';
import logoNoText from '$lib/assets/immich-logo.svg';
import { content as alternativeLogo } from '$lib/assets/immich-logo.json';
- import { Theme } from '$lib/utils/theme';
import { colorTheme } from '$lib/stores/preferences.store';
import { DateTime } from 'luxon';
import type { HTMLImgAttributes } from 'svelte/elements';
import { t } from 'svelte-i18n';
+ import { Theme } from '$lib/constants';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface $$Props extends HTMLImgAttributes {
diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte
index 036da41e1e..db5f520c87 100644
--- a/web/src/lib/components/shared-components/map/map.svelte
+++ b/web/src/lib/components/shared-components/map/map.svelte
@@ -6,7 +6,7 @@
import Icon from '$lib/components/elements/icon.svelte';
import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
- import { Theme } from '$lib/utils/theme';
+ import { Theme } from '$lib/constants';
import { getServerConfig, type MapMarkerResponseDto } from '@immich/sdk';
import mapboxRtlUrl from '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js?url';
import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js';
diff --git a/web/src/lib/components/shared-components/theme-button.svelte b/web/src/lib/components/shared-components/theme-button.svelte
index 8fee1b2bed..81622174e2 100644
--- a/web/src/lib/components/shared-components/theme-button.svelte
+++ b/web/src/lib/components/shared-components/theme-button.svelte
@@ -2,7 +2,7 @@
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
import CircleIconButton, { type Padding } from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store';
- import { Theme } from '$lib/utils/theme';
+ import { Theme } from '$lib/constants';
import { t } from 'svelte-i18n';
$: icon = $colorTheme.value === Theme.LIGHT ? moonPath : sunPath;
diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts
index ad51935465..8462596806 100644
--- a/web/src/lib/constants.ts
+++ b/web/src/lib/constants.ts
@@ -102,6 +102,12 @@ export const timeBeforeShowLoadingSpinner: number = 100;
export const timeDebounceOnSearch: number = 300;
+// should be the same values as the ones in the app.ts
+export enum Theme {
+ LIGHT = 'light',
+ DARK = 'dark',
+}
+
export const fallbackLocale = {
code: 'en-US',
name: 'English (US)',
diff --git a/web/src/lib/stores/preferences.store.ts b/web/src/lib/stores/preferences.store.ts
index 11ae70e56b..16a0da08b8 100644
--- a/web/src/lib/stores/preferences.store.ts
+++ b/web/src/lib/stores/preferences.store.ts
@@ -1,10 +1,14 @@
import { browser } from '$app/environment';
-import { defaultLang } from '$lib/constants';
+import { defaultLang, Theme } from '$lib/constants';
import { getPreferredLocale } from '$lib/utils/i18n';
-import { colorThemeKeyName, Theme, type ThemeSetting } from '$lib/utils/theme';
import { persisted } from 'svelte-local-storage-store';
import { get } from 'svelte/store';
+export interface ThemeSetting {
+ value: Theme;
+ system: boolean;
+}
+
export const handleToggleTheme = () => {
const theme = get(colorTheme);
theme.value = theme.value === Theme.DARK ? Theme.LIGHT : Theme.DARK;
@@ -20,7 +24,8 @@ const initTheme = (): ThemeSetting => {
const initialTheme = initTheme();
-export const colorTheme = persisted(colorThemeKeyName, initialTheme, {
+// The 'color-theme' key is also used by app.ts to prevent FOUC on page load.
+export const colorTheme = persisted('color-theme', initialTheme, {
serializer: {
parse: (text: string): ThemeSetting => {
const parsedText: ThemeSetting = JSON.parse(text);
diff --git a/web/src/lib/utils/app.ts b/web/src/lib/utils/app.ts
index 96e9a0adf0..8e1b904e9b 100644
--- a/web/src/lib/utils/app.ts
+++ b/web/src/lib/utils/app.ts
@@ -1,11 +1,17 @@
-/*
- * we don't want to deal with imports so that import should be
- * replaced by the actual content of the file before fouc.ts is transpiled
- *
- */
-import { colorThemeKeyName, Theme, type ThemeSetting } from '$lib/utils/theme';
+// should be the same values as the one in perferences.store.ts
+interface ThemeSetting {
+ value: Theme;
+ system: boolean;
+}
-const storedTheme = localStorage.getItem(colorThemeKeyName);
+// should be the same values as the ones in constants.ts
+enum Theme {
+ LIGHT = 'light',
+ DARK = 'dark',
+}
+
+// should be the same key as the one in preferences.store.ts
+const storedTheme = localStorage.getItem('color-theme');
const theme: ThemeSetting = storedTheme ? JSON.parse(storedTheme) : { value: Theme.LIGHT, system: true };
const themeValue = theme.system && window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : theme.value;
diff --git a/web/src/lib/utils/theme.ts b/web/src/lib/utils/theme.ts
deleted file mode 100644
index 8f4a9db658..0000000000
--- a/web/src/lib/utils/theme.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export interface ThemeSetting {
- value: Theme;
- system: boolean;
-}
-
-export enum Theme {
- LIGHT = 'light',
- DARK = 'dark',
-}
-
-export const colorThemeKeyName = 'color-theme';
diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte
index cc2f821537..5a6610609e 100644
--- a/web/src/routes/+layout.svelte
+++ b/web/src/routes/+layout.svelte
@@ -8,8 +8,8 @@
import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte';
- import { Theme, type ThemeSetting } from '$lib/utils/theme';
- import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store';
+ import { Theme } from '$lib/constants';
+ import { colorTheme, handleToggleTheme, type ThemeSetting } from '$lib/stores/preferences.store';
import { serverConfig } from '$lib/stores/server-config.store';
import { user } from '$lib/stores/user.store';
import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket';