T
TanStack2mo ago
flat-fuchsia

Better way to get theme?

Im accessing the theme cookie on __root during loader, but every client-side navigation triggers it.
export const Route = createRootRouteWithContext<RouterAppContext>()({
component: RootComponent,
loader: () => getThemeServerFn(),
});

function RootComponent() {
const data = Route.useLoaderData();

return <ThemeProvider theme={data}>{/* */}</ThemeProvider>;
}

function RootDocument({children}: Readonly<{children: React.ReactNode}>) {
const theme = Route.useLoaderData();

return (
<html lang="en" className={theme} suppressHydrationWarning>
{/* */}
</html>
);
}
export const Route = createRootRouteWithContext<RouterAppContext>()({
component: RootComponent,
loader: () => getThemeServerFn(),
});

function RootComponent() {
const data = Route.useLoaderData();

return <ThemeProvider theme={data}>{/* */}</ThemeProvider>;
}

function RootDocument({children}: Readonly<{children: React.ReactNode}>) {
const theme = Route.useLoaderData();

return (
<html lang="en" className={theme} suppressHydrationWarning>
{/* */}
</html>
);
}
import {createServerFn} from "@tanstack/react-start";
import {getCookie, setCookie} from "@tanstack/react-start/server";

type Theme = "dark" | "light";

const storageKey = "ui-theme";

export const getThemeServerFn = createServerFn().handler(async () => {
return (getCookie(storageKey) || "light") as Theme;
});

export const setThemeServerFn = createServerFn({method: "POST"})
.validator((data: unknown) => {
if (typeof data != "string" || (data != "dark" && data != "light")) {
throw new Error("Invalid theme provided");
}
return data as Theme;
})
.handler(async ({data}) => {
setCookie(storageKey, data);
});
import {createServerFn} from "@tanstack/react-start";
import {getCookie, setCookie} from "@tanstack/react-start/server";

type Theme = "dark" | "light";

const storageKey = "ui-theme";

export const getThemeServerFn = createServerFn().handler(async () => {
return (getCookie(storageKey) || "light") as Theme;
});

export const setThemeServerFn = createServerFn({method: "POST"})
.validator((data: unknown) => {
if (typeof data != "string" || (data != "dark" && data != "light")) {
throw new Error("Invalid theme provided");
}
return data as Theme;
})
.handler(async ({data}) => {
setCookie(storageKey, data);
});
Any better approach to just do it one time ?
4 Replies
dependent-tan
dependent-tan3w ago
Data Loading | TanStack Router React Docs
Data loading is a common concern for web applications and is related to routing. When loading a page for your app, it's ideal if all of the page's async requirements are fetched and fulfilled as early...
dependent-tan
dependent-tan3w ago
Especially this:
No description
adverse-sapphire
adverse-sapphire3w ago
i use an inline script like this https://github.com/dotnize/react-tanstarter/blob/2b19669206aa5a7b16a2520297c2051dc579f873/src/routes/__root.tsx#L66-L71 or with a custom theme provider: https://github.com/dotnize/react-tanstarter/blob/main/src/components/theme-provider.tsx#L94-L100
<ScriptOnce>
{`document.documentElement.classList.toggle(
'dark',
localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
)`}
</ScriptOnce>
<ScriptOnce>
{`document.documentElement.classList.toggle(
'dark',
localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
)`}
</ScriptOnce>
it's client-side and executes very early so there is no flash/FOUC
inland-turquoise
inland-turquoise3w ago
you can use react query to cache the result in the loader or beforeLoad

Did you find this page helpful?