theme-change
theme-change copied to clipboard
Issue integrating states with theme-change into React + TailwindCSS + DaisyUI FIXED
To make this work I had to make a handleThemeChange
function to control the use of window.localStoreage.getItem
and pass that into a 'if' statement controlling the setTheme
. I am adding the toggle option to the Navbar of my app.
I had a bit of a unique scenario I needed to handle with my app. I wanted the logo for our site to change based on the Light or Dark theme. I have 2 variants of our logo that look better in each theme.
Firstly you need to make sure you have your tailwind.config.js
file updated with DaisyUI and configured properly. This is where the light
and dark
theme toggles will come from below and they MUST match:
plugins: [require("@tailwindcss/typography"), require("daisyui")],
daisyui: {
themes: [
"light",
"dark",
],
darkTheme: "dark",
base: true,
styled: true,
utils: true,
rtl: false,
prefix: "",
logs: true,
},
Follow like the readme instructions say:
import { themeChange } from 'theme-change';
Additionally, I am using the react-feather
icons so I added:
import { Sun, Moon } from 'react-feather';
Just inside the page component I 'InitializeState' of the theme and logo:
const Navbar: React.FC = () => {
// Initialize state
// First, we try to get the current theme from local storage. If there's no theme
// stored yet, we default to 'light'.
const initialTheme = window.localStorage.getItem('theme') || 'light';
// We then initialize our 'theme' state variable with the initial theme.
const [theme, setTheme] = useState(initialTheme);
// Based on the initial theme, we set the initial logo. If the theme is 'dark',
// we set the light logo, and vice versa.
const initialLogoName = initialTheme === 'dark' ? logoNameLight : logoNameDark;
// We then initialize our 'logoName' state variable with the initial logo name.
const [logoName, setLogoName] = useState(initialLogoName);
Next we have to instantiate the useEffect
by doing this:
useEffect(() => {
// In the 'useEffect' hook that runs on component mount, we get the current theme
// from local storage again (it might have changed since the component initialized).
// Again, if there's no theme stored yet, we default to 'light'.
const currentTheme = window.localStorage.getItem('theme') || 'light';
// We then update our 'theme' state variable with the current theme.
setTheme(currentTheme);
// Finally, we call 'themeChange' to update the actual theme of the app. If the
// theme is 'dark', we pass 'true' to 'themeChange', and 'false' otherwise.
themeChange(currentTheme === 'dark');
}, []);
To handle the changing of the theme and logo swap I created the function, here is the snippet of the handleThemeChange
:
const handleThemeChange = () => {
// When the theme change handler is called, we first get the current theme from
// our 'theme' state variable.
const currentTheme = theme;
if (currentTheme === 'dark') {
// If the current theme is 'dark', we switch to the 'light' theme. We update
// the theme in local storage, the 'theme' state variable, and call 'themeChange'
// with 'false' to switch the actual theme of the app. Finally, we switch the
// logo to the dark version.
window.localStorage.setItem('theme', 'light');
setTheme('light');
themeChange(false);
setLogoName(logoNameDark);
} else {
// If the current theme is not 'dark' (so it's 'light'), we switch to the 'dark'
// theme. We update the theme in local storage, the 'theme' state variable, and
// call 'themeChange' with 'true' to switch the actual theme of the app. Finally,
// we switch the logo to the light version.
window.localStorage.setItem('theme', 'dark');
setTheme('dark');
themeChange(true);
setLogoName(logoNameLight);
}
}
In the body of the jsx under the return (
I made my appropriate updates to the correct sections of the Navbar I needed.
You can see here in my section I display the Navbar Logo:
<div className="navbar-center">
{/* Logo image works with the themeChange to set correct logo */}
<img src={logoName} alt="logo" onClick={home} className="w-40" />
</div>
Here you can see the use of the logoName
that comes from the state of the handleThemeChange
function.
For the actual Sun/Moon toggle for Light and Dark I implemented like this:
{/* Dark mode toggle button */}
<button
data-toggle-theme="dark,light"
data-act-class="ACTIVECLASS"
onClick={handleThemeChange}
className="text-white btn btn-ghost btn-circle"
>
{theme === 'light' ? (
<Moon className="h-5 w-5" strokeWidth="2" />
) : (
<Sun className="h-5 w-5" strokeWidth="2" />
)}
</button>