Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First call on setTheme not working #220

Open
mertafor opened this issue Sep 18, 2023 · 6 comments
Open

First call on setTheme not working #220

mertafor opened this issue Sep 18, 2023 · 6 comments

Comments

@mertafor
Copy link

mertafor commented Sep 18, 2023

If it's first launch of the website and first call on setTheme, the function doesn't work. I noticed it only sets "theme" in localStorage on first call then applies the theme change on second call. What's necessary is checking localStorage and "theme" should be set if it is not set in localStorage yet on first load.

Here is the code I use to prevent this issue in case it helps someone. But I'd appreciate an official solution without interfering library's way of managing it :

export const ThemeSwitcher = () => {
  const { theme, setTheme } = useTheme();

  useEffect(() => {
    const checkLocalStorage = localStorage.getItem("theme");
    if (!checkLocalStorage) {
      const checkDarkTheme = window.matchMedia("(prefers-color-scheme: dark)");
      setTheme(checkDarkTheme ? "dark" : "light");
    }
  }, [setTheme]);

  return (
    <HeaderButton>
      <button
        onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      >
        <IoSunny className="hidden dark:block" />
        <IoMoon className="block dark:hidden" />
      </button>
    </HeaderButton>
  );
};

Of course I'd appreciate if there is already a built-in solution but it should have been the default behavior already anyways.

@agentfox
Copy link

It happens when your default theme is "system", right? Try to change the default theme to "light" or "dark". That would be a workaround for now.

@abdullahmehboob20s
Copy link

I am also same issue.

@gustaveWPM
Copy link

gustaveWPM commented Nov 19, 2023

Hello, same issue here!

When I visit my website for the first time, the "theme" field in my LocalStorage is missing.

Maybe related to this, as said @agentfox

It happens when your default theme is "system", right? Try to change the default theme to "light" or "dark". That would be a workaround for now.

@mertafor
Copy link
Author

mertafor commented Nov 19, 2023

The workaround I used before was causing visitor to stuck in their previous theme. For example if website visited while visitor's system theme is dark, it stays dark even after visitor changes their system theme.

I improved my original workaround by using another state in order to prevent storing theme in local storage until visitor clicks change theme button. State helps to find out the new theme value on running setTheme:

"use client"

import { useTheme } from "next-themes";

export const ThemeSwitcher = () => {
  const { theme, setTheme } = useTheme();
  const [changeThemeValue, setChangeThemeValue] = React.useState<string>();

  useEffect(() => {
    setChangeThemeValue(theme === "dark" ? "light" : "dark");
  }, [theme]);

  useEffect(() => {
    // detect if current system theme is dark
    const checkDarkTheme = window.matchMedia("(prefers-color-scheme: dark)");
   // Find new theme value for setTheme
    const newValue =
      theme === "dark"
        ? "light"
        : theme === "light"
        ? "dark"
        : checkDarkTheme
        ? "light"
        : "dark";
    setChangeThemeValue(newValue);
  }, []);

  return (
      <button
        title="Dark/Light"
        onClick={() => setTheme(changeThemeValue ?? "dark")}
      >
        Change theme
      </button>
  );
};

Hope it helps.

@elbaley
Copy link

elbaley commented Nov 20, 2023

The workaround I used before was causing visitor to stuck in their previous theme. For example if website visited while visitor's system theme is dark, it stays dark even after visitor changes their system theme.

I improved my original workaround by using another state in order to prevent storing theme in local storage until visitor clicks change theme button. State helps to find out the new theme value on running setTheme:

"use client"

import { useTheme } from "next-themes";

export const ThemeSwitcher = () => {
  const { theme, setTheme } = useTheme();
  const [changeThemeValue, setChangeThemeValue] = React.useState<string>();

  useEffect(() => {
    setChangeThemeValue(theme === "dark" ? "light" : "dark");
  }, [theme]);

  useEffect(() => {
    // detect if current system theme is dark
    const checkDarkTheme = window.matchMedia("(prefers-color-scheme: dark)");
   // Find new theme value for setTheme
    const newValue =
      theme === "dark"
        ? "light"
        : theme === "light"
        ? "dark"
        : checkDarkTheme
        ? "light"
        : "dark";
    setChangeThemeValue(newValue);
  }, []);

  return (
      <button
        title="Dark/Light"
        onClick={() => setTheme(changeThemeValue ?? "dark")}
      >
        Change theme
      </button>
  );
};

Hope it helps.

Thanks for the workaround! I noticed that if the initial system mode is "light" clicking the button doesn't switch to "dark" as intended. This is because we are not checking dark mode properly, I've addressed this by modifying checkDarkTheme:

const checkDarkTheme = window.matchMedia("(prefers-color-scheme: dark)").matches;

@gustaveWPM
Copy link

I'm using the same workaround now, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants