[BUG] - Any Modal/Popover is outside the provider by default. `Select` does not have `portalContainer` to fix this.
NextUI Version
2.2.9
Describe the bug
Being outside of the Provider, causes the content to have the wrong theme. Providing the portalContainer-prop for Select would be a fix, but it is kind of anyoing to have to fix every Modal like component like than.
Your Example Website or App
https://stackblitz.com/edit/vitejs-vite-ih6tja?file=src%2Fmain.tsx
Steps to Reproduce the Bug or Issue
- Init a bare react + vite project
- Add a select
- set to dark mode
- open select and get flashbanged
Expected behavior
Select popover should be in dark mode too
Screenshots or Videos
Operating System Version
macOS
Browser
Firefox
I'm running into same issue. It also seems that even when I'm adding portalContainer={document.body} to the Select component, the children components aren't accessing my theme. Any updates?
I'm having the same problem in Autocomplete, when my header has a fixed position
Is this fundamentally difficult to fix, on a technical level?
I’m considering both NextUI and NextUI Pro for an upcoming project and ran into this pretty early on (my Dropdowns are light even though the theme is dark), and it seems like it’s going to be a bit of a pain to need theming logic for every dropdown, select component etc., instead of just being able to set it once on the outermost container.
I could of course create themed versions of these components to avoid duplicating the theming logic, but still, it’s not optimal to need such a workaround. Curious if a fix is at least technically possible.
I was able to solve this by setting the dark / light class on the body element instead of the app wrapper. In doing so the class also applies to the sibling element for the portal/popover, which fixes the styling.
It’s a little bit more involved than just setting the class on the app wrapper, since you need to manipulate the class of an element outside the app boundary (for example by using a useLayoutEffect hook to apply the updated body class), but not a whole lot. If the docs mentioned it I wouldn’t even consider it a problem.
I had the same bug but I managed to fix it. I created a provider and in it I have the theme logic.
Root Layout
Theme Provider
I solved it by simply changing the <main> tag to <body> within my theme provider.
I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark");
const toggleTheme = () => {
const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark";
document.querySelector("body")?.classList.replace(theme, newTheme);
localStorage.setItem("theme", newTheme);
setTheme(newTheme);
}
useEffect(() => {
document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background");
})
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}
I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.
export default function ThemeProvider({ children }: { children: React.ReactNode }) { const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark"); const toggleTheme = () => { const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark"; document.querySelector("body")?.classList.replace(theme, newTheme); localStorage.setItem("theme", newTheme); setTheme(newTheme); } useEffect(() => { document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background"); }) return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ) }
I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.
export default function ThemeProvider({ children }: { children: React.ReactNode }) { const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark"); const toggleTheme = () => { const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark"; document.querySelector("body")?.classList.replace(theme, newTheme); localStorage.setItem("theme", newTheme); setTheme(newTheme); } useEffect(() => { document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background"); }) return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ) }
Hey. Thank you soo much for this. i was looking everywhere until i found this that help me a lot. Thanks ❤️
I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.
export default function ThemeProvider({ children }: { children: React.ReactNode }) { const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark"); const toggleTheme = () => { const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark"; document.querySelector("body")?.classList.replace(theme, newTheme); localStorage.setItem("theme", newTheme); setTheme(newTheme); } useEffect(() => { document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background"); }) return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ) }
Awesome. Thank you very much.