Help Me Stop Theme Flashing In SolidStart

I built a theme switcher using SolidStart. It works great, with one exception. On page reload, the theme flickers or flashes. It's happening because I'm using SSR mode. As a result, my ThemeProvider doesn't load the theme until the page loads. I believe I need to check the theme on the server so I can set the theme before my page loads using something like https://github.com/donavon/use-dark-mode/blob/develop/noflash.js.txt. Next.js has the next-themes package that does this for you. https://github.com/pacocoursey/next-themes I'm wondering how I can do this? Can anyone point me in the right direction? Or share some example code? I've pasted my ThemeProvider code in the next post:
GitHub
GitHub - pacocoursey/next-themes: Perfect Next.js dark mode in 2 li...
Perfect Next.js dark mode in 2 lines of code. Support System preference and any other theme with no flashing - pacocoursey/next-themes
17 Replies
ChrisThornham
ChrisThornham3mo ago
import { JSX, createContext, createSignal, onMount } from "solid-js";
import { lightTheme, darkTheme } from "../../qs.config";

export const ThemeContext = createContext();

interface themeProps {
children?: JSX.Element | JSX.Element[];
}

export function ThemeProvider(props: themeProps) {
// STATE
const [isDarkMode, setIsDarkMode] = createSignal(false);

// LIFECYCLE
onMount(() => {
// see if there is a theme in local storage.
const theme = localStorage.getItem("theme");

// if not, find the users preferred theme.
if (!theme) {
if (window.matchMedia("(prefers-color-scheme: dark)")) {
setIsDarkMode(true);
toggleTheme(darkTheme);
localStorage.setItem("theme", darkTheme);
} else {
setIsDarkMode(false);
toggleTheme(lightTheme);
localStorage.setItem("theme", lightTheme);
}
} else {
if (theme === darkTheme) {
setIsDarkMode(true);
toggleTheme(darkTheme);
} else {
setIsDarkMode(false);
toggleTheme(lightTheme);
}
}
});

// FUNCTIONS
function toggleTheme(colorTheme: string) {
// update the darkmode state.
setIsDarkMode(!isDarkMode());
// data theme is for Daisy
document.documentElement.setAttribute("data-theme", colorTheme);
// Save the user's preference in localStorage or a cookie for persistence
localStorage.setItem("theme", colorTheme);
}

// PROVIDER VALUES
const sharedValues = {
lightTheme,
darkTheme,
isDarkMode,
toggleTheme,
};

// TSX

return (
<ThemeContext.Provider value={sharedValues}>
{props.children}
</ThemeContext.Provider>
);
}
import { JSX, createContext, createSignal, onMount } from "solid-js";
import { lightTheme, darkTheme } from "../../qs.config";

export const ThemeContext = createContext();

interface themeProps {
children?: JSX.Element | JSX.Element[];
}

export function ThemeProvider(props: themeProps) {
// STATE
const [isDarkMode, setIsDarkMode] = createSignal(false);

// LIFECYCLE
onMount(() => {
// see if there is a theme in local storage.
const theme = localStorage.getItem("theme");

// if not, find the users preferred theme.
if (!theme) {
if (window.matchMedia("(prefers-color-scheme: dark)")) {
setIsDarkMode(true);
toggleTheme(darkTheme);
localStorage.setItem("theme", darkTheme);
} else {
setIsDarkMode(false);
toggleTheme(lightTheme);
localStorage.setItem("theme", lightTheme);
}
} else {
if (theme === darkTheme) {
setIsDarkMode(true);
toggleTheme(darkTheme);
} else {
setIsDarkMode(false);
toggleTheme(lightTheme);
}
}
});

// FUNCTIONS
function toggleTheme(colorTheme: string) {
// update the darkmode state.
setIsDarkMode(!isDarkMode());
// data theme is for Daisy
document.documentElement.setAttribute("data-theme", colorTheme);
// Save the user's preference in localStorage or a cookie for persistence
localStorage.setItem("theme", colorTheme);
}

// PROVIDER VALUES
const sharedValues = {
lightTheme,
darkTheme,
isDarkMode,
toggleTheme,
};

// TSX

return (
<ThemeContext.Provider value={sharedValues}>
{props.children}
</ThemeContext.Provider>
);
}
jer3m01
jer3m013mo ago
you can store the theme in cookies and access during SSR
ChrisThornham
ChrisThornham3mo ago
@jer3m01 Thank you. Can you point to any documentation, examples, or references that would show me how to store cookies and access them during SSR?
jer3m01
jer3m013mo ago
You can import from vinxi: import { getCookie } from "vinxi/server"; To set the theme cookie do it client side when the users selects a theme or when you mount (as you did above) Here's an example https://github.com/kobaltedev/kobalte/blob/main/apps/docs/src/app.tsx#L42
ChrisThornham
ChrisThornham3mo ago
Sweet! Let me read through that after lunch. Much appreciated.
jer3m01
jer3m013mo ago
On new versions of Solid Start the import might be import { getCookie } from "vinxi/http";
ChrisThornham
ChrisThornham3mo ago
You said: set the theme cookie client side when I mount I'm guessing I'd do that with setCookie from vinxi/http in a "use server" function?
peerreynders
peerreynders3mo ago
I'd actually read all cookie/session data in middleware and then stuff it into getRequestEvent.locals Then check isServer before you try to access getRequestEvent(). Just be careful as it may return undefined if you try to use it too early (like when the module first loads).
SolidStart Release Candidate Documentation
SolidStart Release Candidate Documentation
Early release documentation and resources for SolidStart Release Candidate
peerreynders
peerreynders3mo ago
GitHub
solid-start-demo-login/src/middleware.ts at restart · peerreynders/...
SolidStart seed project with simple user management for demonstration projects. - peerreynders/solid-start-demo-login
GitHub
solid-start-sse-chat/src/middleware.ts at restart · peerreynders/so...
Basic Chat demonstration with server-sent events (SSE) - peerreynders/solid-start-sse-chat
peerreynders
peerreynders3mo ago
Handle Cookie - h3
Use cookies to store data on the client.
Handle Session - h3
Remember your users using a session.
SolidStart Release Candidate Documentation
SolidStart Release Candidate Documentation
Early release documentation and resources for SolidStart Release Candidate
peerreynders
peerreynders3mo ago
Depends how you are managing the cookie.
ChrisThornham
ChrisThornham3mo ago
Excellent! I'll read through this as well.
peerreynders
peerreynders3mo ago
Cookies can be httpOnly which means only the server can access the content. Even when it's httpOnly, if you are not using it for authentication but just correlation, you can just initialize the cookie in middleware when you find the request doesn't have one. When the request to change the theme comes in the middleware can update the value and just finish the request/response.
peerreynders
peerreynders3mo ago
MDN Web Docs
Using HTTP cookies - HTTP | MDN
An HTTP cookie (web cookie, browser cookie) is a small piece of data that a server sends to a user's web browser. The browser may store the cookie and send it back to the same server with later requests. Typically, an HTTP cookie is used to tell if two requests come from the same browser—keeping a user logged in, for example. It remembers st...
peerreynders
peerreynders3mo ago
Of course you could just bypass all that and go by prefers-color-scheme instead (web.dev).
MDN Web Docs
prefers-color-scheme - CSS: Cascading Style Sheets | MDN
The prefers-color-scheme CSS media feature is used to detect if a user has requested light or dark color themes. A user indicates their preference through an operating system setting (e.g. light or dark mode) or a user agent setting.
peerreynders
peerreynders3mo ago
Jen Simmons
front-end.social
Jen Simmons (@jensimmons@front-end.social)
I used the light-dark function quite a lot in recent demos. It makes implementing dark mode so easy! Drop this in your base styles:
:root {
color-scheme: light dark;
}
:root {
color-scheme: light dark;
}
Then, any time you define a color, do it like this…. to assign the first color to light mode, and the second to the dark mode. ``` color: light-dark(#000, #fff); back...
MDN Web Docs
light-dark() - CSS: Cascading Style Sheets | MDN
The light-dark() CSS function enables setting two colors for a property - returning one of the two colors options by detecting if the developer has set a light or dark color scheme or the user has requested light or dark color theme - without needing to encase the theme colors within a prefers-color-scheme media feature query. Users are able ...
ChrisThornham
ChrisThornham3mo ago
I used the light-dark function quite a lot in recent demos. It makes implementing dark mode so easy! I've done that in the past, and it works well, but it takes a lot of setup when working with multiple themes.
I'm using daisyUI for this project to simplify creating themes. Currently, I can set the light and dark theme for a project by changing two variables in a config file. It's pretty cool.