RecipesRecipe
SSR Zero-flash Bootstrap
Prevent the flash of unstyled content by resolving the theme on the server and embedding it in the initial render.
- Goal
- Prevent the flash of unstyled content by resolving the theme on the server and embedding it in the initial render.
- Prerequisites
- @themvora/core installed
- Framework adapter (e.g., @themvora/next, @themvora/nuxt)
- Theme configuration with cookie persistence
- Stack
- Next.js / Nuxt / Astro (SSR-capable frameworks)
- Result
- Zero-flash theme selection on first paint
1Install
Pick your package manager — the command updates to match.
@themvora/corepnpm add @themvora/coreUsing a framework? Its adapter is a separate package — see the framework guides for the provider setup, then come back for the pattern.
2Implement
Drop this into your project. It is the whole pattern — no other setup is required.
import {
createThemeBootstrapScript,
getBuiltInThemes,
} from "@themvora/core";
export async function BootstrapScript({ resolvedTheme }) {
const script = createThemeBootstrapScript({
themes: getBuiltInThemes(),
defaultTheme: resolvedTheme,
});
return (
<script dangerouslySetInnerHTML={{ __html: script }} key="theme-bootstrap" />
);
}3Why this works
The createThemeBootstrapScript function generates a synchronous script that runs before any CSS loads. It sets the correct CSS variables on the <html> element, preventing the browser from displaying the unstyled default theme. The script: 1. Resolves the theme from cookies, headers, or URL parameters 2. Sets all CSS variables on document.documentElement 3. Does not require any Themvora runtime code to load Make sure to: - Include the script in <head> before stylesheets - Use the same theme resolution logic server-side as client-side - Render the data-theme* attributes the script writes on <html> too, so React has nothing to report as a mismatch
4Verify
- The page loads with no console errors after the change.
- The outcome holds: zero-flash theme selection on first paint
- Toggling the theme in your app still works, and the state survives a reload if persistence is configured.
- Reduced-motion users see no animation if you enabled transitions.