Closed osilkin98 closed 1 year ago
hi @osilkin98, This dark theme toggle is also not working on the official https://flowbite-react.com/ site. I have attached the video you can check it out and confirm.
Hello @osilkin98 and @Tejas9535,
This issue appeared because Flowbite React no longer automatically stores the theme mode in localStorage due to this GDPR issue (https://github.com/themesberg/flowbite-react/issues/546), however, I believe we will provide a script or guide to show you how you can store this in localStorage yourself and decide to show a cookie modal or not.
From what we've discussed even in the EU it is not quite necessary to use the cookie modal just for storing dark or light mode using the localStorage because it is usually regarded as a necessary cookie, however, it is better to give this power of decision back to the end-user of Flowbite React.
@tulup-conner @rluders firstly we do need to update our own docs (or at least the new one with Next.js) so that the dark or light mode will persist and we'll just use localStorage for that. Otherwise, we should write a "dark mode" guide similar to what is already available on Flowbite so that Flowbite React users can easily implement them.
Cheers, Zoltan
This is an unrelated issue, it's been a problem with the development server for at least as long as I have been working on the project. It was a bug in the local storage implementation, and now that we aren't doing that at all anymore, I guess actually it still won't work anyway now :(
Folks, the user must decide where he will store the theme. He can use localStorage or cookies, but he needs to implement them. The implementation isn't that complicated. At the same time, I think that we are missing a hook here, onChange
at the toggle or at the ThemeContext.
function storeThemeSomewhere() {
// cookie or localstorage
}
const [mode, setMode, toggleMode] = useThemeMode();
let isDark = true // load it from where you store
setMode(isDark ? 'dark' : 'light');
<Flowbite>
<DarkThemeToggle onChange={(theme) => storeThemeSomewhere(theme) } />
</Flowbite>
☝️ I didn't test it, this is a raw code, just to try to demonstrate the idea.
Hi, I have tried to come up with a solution for it, but it just doesn't work.
const FlowbiteContext: FC<PropsWithChildren> = function ({ children }) {
const [mode, , toggleMode] = useThemeMode(true);
useEffect(() => {
localStorage.setItem('theme', mode);
return (() => {
localStorage.removeItem('theme');
})
}, [mode])
useEffect(() => {
if (typeof window === 'undefined') return
const theme = localStorage.getItem('theme');
if (theme === mode) return;
toggleMode()
}, [mode, toggleMode])
return <Flowbite theme={{ theme, usePreferences: true }}>{children}</Flowbite>;
};
this just doesn't work I have tried many other solutions as well.
Hi @zoltanszogyenyi,
I understand the concerns around GDPR compliance and appreciate the efforts being made to provide a guide for storing theme mode in localStorage. However, I would like to raise a few concerns regarding the user experience and the impact on developers.
While I agree that GDPR compliance is important, I believe that we should also consider the user experience and avoid adding unnecessary pop-ups and hurdles for users. Perhaps we could explore alternative ways of complying with GDPR regulations while still maintaining a smooth and intuitive user experience.
Additionally, I understand the importance of GDPR compliance, but it can be challenging for developers to balance compliance with providing essential features that users have come to expect, such as dark/light mode. Is there a way to modify the implementation to comply with GDPR while also providing this feature by default, with an opt-out option for GDPR compliance?
I've come up with a (hacky) workaround to address the dark mode handling using a higher-order component (HOC). This HOC handles the dark mode logic before the Flowbite component renders, ensuring that the correct dark mode state will be applied regardless of the problem that apparently comes from within the Flowbite component. Note that this solution does not retain the previously set state (using e.g. localstorage), but you can modify it such that it does. Right now, only the window's preferred media type is matched on component render.
Here's the HOC implementation:
// withDarkMode.tsx
import React, { useEffect } from 'react';
const withDarkMode = <T extends React.ElementType>(WrappedComponent: T) => {
type Props = React.ComponentProps<T>;
return (props: Props) => {
useEffect(() => {
const setDarkMode = () => {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
};
setDarkMode();
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMediaQuery.addEventListener('change', setDarkMode);
return () => {
darkModeMediaQuery.removeEventListener('change', setDarkMode);
};
}, []);
return React.createElement(WrappedComponent, props);
};
};
export default withDarkMode;
To use the HOC, wrap the Flowbite component with withDarkMode:
// App.tsx or any other component that uses Flowbite
import React from 'react';
import Flowbite from 'flowbite-react';
import withDarkMode from './withDarkMode';
const FlowbiteWithDarkMode = withDarkMode(Flowbite);
const App: React.FC = () => {
return (
<div>
<FlowbiteWithDarkMode>
{/* Your app content */}
</FlowbiteWithDarkMode>
</div>
);
};
export default App;
The withDarkMode HOC adds an additional layer of dark mode handling that runs before the Flowbite component renders, ensuring that the correct dark mode state is applied even if there's an issue in the Flowbite component's logic.
That makes sense. I think the simplest solution would be to add a function to the dark theme toggle that lets you do something when the theme changes, like
let dark = isClient() && (localStorage.getItem("theme") === "dark" || window.matchMedia("(prefers-color-scheme: dark)").matches); // read from localStorage on first load
<Flowbite theme={{ dark }}>
<DarkThemeToggle onClick={(newTheme) => { // -> ("dark")
localStorage.setItem("theme", newTheme); // persist to localStorage when changed
}} />
</Flowbite>
Pretty easy to add this change on the library's end. Thoughts?
let dark = isClient() && (localStorage.getItem("theme") === "dark"
const [mode, setMode, toggleMode] = useThemeMode();
const [dark, setDark] = useState(false);
useEffect(()=>{
let dark =
localStorage.getItem("theme") === "dark"
setDark(dark);
}, [])
<Flowbite theme={{ dark }}>
<DarkThemeToggle onClick={(newTheme) => { // -> ("dark")
toggleMode();
let newMode = mode == "dark"?"light":"dark";
localStorage.setItem("theme", newMode ); // persist to localStorage when changed
}} />
</Flowbite>
let dark = isClient() && (localStorage.getItem("theme") === "dark" const [mode, setMode, toggleMode] = useThemeMode(); const [dark, setDark] = useState(false); useEffect(()=>{ let dark = localStorage.getItem("theme") === "dark" setDark(dark); }, []) <Flowbite theme={{ dark }}> <DarkThemeToggle onClick={(newTheme) => { // -> ("dark") toggleMode(); let newMode = mode == "dark"?"light":"dark"; localStorage.setItem("theme", newMode ); // persist to localStorage when changed }} /> </Flowbite>
I had to modify the above code slightly for it to work:
const [mode, setMode, toggleMode] = useThemeMode();
const [dark, setDark] = useState(false);
useEffect(() => {
let dark =
localStorage.getItem("theme") === "dark"
setDark(dark);
}, [])
return (
<Flowbite theme={{ dark }}>
<DarkThemeToggle onClick={() => {
toggleMode();
let newMode = mode == "dark" ? "light" : "dark";
localStorage.setItem("theme", newMode);
}} />
<Header/>
{children}
</Flowbite>
However, I was using a customTheme (in order to customize the individual colors) by doing this:
const customTheme: CustomFlowbiteTheme = {
tab: {
tablist: {
tabitem: {
...
<Flowbite theme={{ theme: customTheme }}>
Now that's not working anymore. Is there any way to modify the above code so that it will pass the dark / light mode to the customTheme instead?
Also, the DarkThemeToggle icon broke in the UI. It will always show the same icon now, although toggling still works.
I'm still facing this issue. Could anyone please help?
I'm still facing this issue. Could anyone please help?
If you are using flowbite and react, this is my solution when you rendering
import { useEffect } from 'react'
import { Flowbite } from 'flowbite-react';
function App() {
useEffect(() => {
const theme = localStorage.getItem('theme');
if (theme === 'dark') {
document.querySelector('html').classList.add('dark');
// Create a click event
const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window });
const buttonElement = document.querySelector('button[aria-label*="Toggle dark mode"]');
buttonElement.dispatchEvent(clickEvent);
} else {
document.querySelector('html').classList.remove('dark');
}
// Function to be executed when you change the "dark" class in the HTML tag
const handleDarkModeChange = (mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const htmlElement = document.querySelector('html');
if (htmlElement.classList.contains('dark')) {
// The "dark" class has been added
localStorage.setItem('theme', 'dark');
} else {
// The "dark" class has been removed
localStorage.setItem('theme', 'light');
}
}
}
};
// Create a new MutationObserver
const htmlElement = document.querySelector('html');
const observer = new MutationObserver(handleDarkModeChange);
// Notice changes in the "dark" class of the HTML tag
observer.observe(htmlElement, { attributes: true, attributeFilter: ['class'] });
}, []);
return (
<Flowbite>
<YourNavbar />
<section>.....</section>
<YourFooter />
</Flowbite>
)
}
export default App
I solved it this way:
const [dark, setDark] = useState(false);
useEffect(() => {
setDark(localStorage.getItem("theme") === "dark");
}, []);
function onThemeToggle() {
localStorage.setItem("theme", dark ? "light" : "dark");
setDark(!dark);
}
...
<DarkThemeToggle onClick={() => onThemeToggle()} />
The only problem still present is screen flickering on load when dark theme is selected.
flowbite: "^1.6.3" flowbite-react": "^0.4.2"
Describe the bug When running Next.js on a local server, the dark/light mode switch doesn't persist between page refreshes.
To Reproduce Steps to reproduce the behavior:
Expected behavior On page refresh, the mode should switch back to what it was previously toggled to. So if a page initialized light and was switch to dark, further page refreshes should bring it back to dark mode.
Screenshots
System information:
Project information:
Additional context Add any other context about the problem here.