diff --git a/.changeset/friendly-pets-reflect.md b/.changeset/friendly-pets-reflect.md
new file mode 100644
index 000000000..e86611b37
--- /dev/null
+++ b/.changeset/friendly-pets-reflect.md
@@ -0,0 +1,5 @@
+---
+"svelte-ux": patch
+---
+
+Add ThemeInit component to prevent flash of unstyled content when SSR is enabled
diff --git a/packages/svelte-ux/src/lib/components/Settings.svelte b/packages/svelte-ux/src/lib/components/Settings.svelte
index bae5e3e24..a2c0952e9 100644
--- a/packages/svelte-ux/src/lib/components/Settings.svelte
+++ b/packages/svelte-ux/src/lib/components/Settings.svelte
@@ -1,9 +1,15 @@
+{#if themeInit}
+
+{/if}
diff --git a/packages/svelte-ux/src/lib/components/ThemeInit.svelte b/packages/svelte-ux/src/lib/components/ThemeInit.svelte
new file mode 100644
index 000000000..6daffa81e
--- /dev/null
+++ b/packages/svelte-ux/src/lib/components/ThemeInit.svelte
@@ -0,0 +1,12 @@
+
+
+
+ {@html headSnippet}
+
diff --git a/packages/svelte-ux/src/lib/components/index.ts b/packages/svelte-ux/src/lib/components/index.ts
index acf035412..50e84ea25 100644
--- a/packages/svelte-ux/src/lib/components/index.ts
+++ b/packages/svelte-ux/src/lib/components/index.ts
@@ -83,6 +83,7 @@ export { default as Tab } from './Tab.svelte';
export { default as Tabs } from './Tabs.svelte';
export { default as TextField } from './TextField.svelte';
export { default as ThemeButton } from './ThemeButton.svelte';
+export { default as ThemeInit } from './ThemeInit.svelte';
export { default as Tilt } from './Tilt.svelte';
export { default as Toggle } from './Toggle.svelte';
export { default as ToggleButton } from './ToggleButton.svelte';
diff --git a/packages/svelte-ux/src/lib/styles/theme.ts b/packages/svelte-ux/src/lib/styles/theme.ts
index aac38dadf..edb9d0872 100644
--- a/packages/svelte-ux/src/lib/styles/theme.ts
+++ b/packages/svelte-ux/src/lib/styles/theme.ts
@@ -20,3 +20,27 @@ export const colorNames = [
'surface-300',
'surface-content',
];
+
+/** Return a script tag that will set the initial theme from localStorage. This allows setting
+ * the theme before anything starts rendering, even when SSR is in use.
+ *
+ * This feels a bit weird compared to just placing the function directly in svelte:head,
+ * but it's the only way to inject the `darkThemes` array into the function.
+ **/
+export function createHeadSnippet(darkThemes: string[]) {
+ function _applyInitialStyle(darkThemes) {
+ let theme = localStorage.getItem('theme');
+ if (theme) {
+ document.documentElement.dataset.theme = theme;
+ if (darkThemes.includes(theme)) {
+ document.documentElement.classList.add('dark');
+ }
+ } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ document.documentElement.classList.add('dark');
+ }
+ }
+
+ let darkThemeList = darkThemes.map((theme) => `'${theme}'`).join(', ');
+
+ return ``;
+}