#Help Me Stop Theme Flashing In SolidStart

22 messages · Page 1 of 1 (latest)

prime remnant
#

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

Perfect Next.js dark mode in 2 lines of code. Support System preference and any other theme with no flashing - pacocoursey/next-themes

#
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>
  );
}
soft shell
#

you can store the theme in cookies and access during SSR

prime remnant
#

@soft shell Thank you.

Can you point to any documentation, examples, or references that would show me how to store cookies and access them during SSR?

soft shell
prime remnant
#

Sweet! Let me read through that after lunch. Much appreciated.

soft shell
#

On new versions of Solid Start the import might be import { getCookie } from "vinxi/http";

prime remnant
#

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?

boreal tide
#

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).

boreal tide
prime remnant
#

Excellent! I'll read through this as well.

boreal tide
#

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.

#
MDN Web Docs

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...

boreal tide
#
MDN Web Docs

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 ...

prime remnant
# boreal tide https://front-end.social/@jensimmons/112209210730117217 [MDN light-dark](https:/...

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.

winged plank
prime remnant
# winged plank How did you manage to do it? I am using daisy ui and i tried writing my own prov...

It's been close to a year since I've worked on this, so my memory is weak.

Here's my full Theme Context provider that stores the theme preference in local storage

import { JSX, createContext, createSignal, onMount } from "solid-js";
import { lightTheme, darkTheme } from "../../../quickstart.config";

// Create and Export the Theme Context
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>
  );
}