-
-
Notifications
You must be signed in to change notification settings - Fork 188
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
How to force theme (app router) #226
Comments
In my case, I have 2 theme providers where one is forced and one is not forced. And in App Router, we can use the grouping folder (group1) and (group2). Put the non forced in (group1) folder and forced one inside the layout of (group2) folder. In this case, the layouts will affects only the pages inside each own groups. |
I have 1 page that needs to be with a forced theme having two global folders looks too much in this case |
I made a provider like this "use client"
import * as React from "react";
import { ThemeProvider as NextJSThemesProvider } from "next-themes";
import { ThemeProviderProps as NextJSThemesProviderProps } from "next-themes/dist/types";
interface ForcedThemeContextProps {
forcedTheme: string | null;
setForcedTheme: React.Dispatch<React.SetStateAction<string | null>>;
}
const ForcedThemeContext = React.createContext<ForcedThemeContextProps | undefined>(undefined);
export function useForcedThemeControl(): ForcedThemeContextProps {
const context = React.useContext(ForcedThemeContext);
if (!context) {
throw new Error("useForcedThemeControl must be used within a ForcedThemeContextProvider");
}
return context;
}
interface ForcedThemeContextProviderProps {
children: React.ReactNode;
}
export function ForcedThemeContextProvider({ children }: ForcedThemeContextProviderProps): JSX.Element {
const [forcedTheme, setForcedTheme] = React.useState<string | null>(null);
return (
<ForcedThemeContext.Provider value={{ forcedTheme, setForcedTheme }}>
{children}
</ForcedThemeContext.Provider>
);
}
interface ThemeSetterProps {
children: React.ReactNode;
}
function DarkTheme({ children }: ThemeSetterProps): JSX.Element {
const { setForcedTheme } = useForcedThemeControl();
React.useEffect(() => {
setForcedTheme("dark");
}, []);
return <>{children}</>;
}
function LightTheme({ children }: ThemeSetterProps): JSX.Element {
const { setForcedTheme } = useForcedThemeControl();
React.useEffect(() => {
setForcedTheme("light");
}, []);
return <>{children}</>;
}
interface CombinedThemeProviderProps extends NextJSThemesProviderProps {
children: React.ReactNode;
}
const CombinedThemeProvider = ({ children, ...props }: CombinedThemeProviderProps): JSX.Element => {
const { forcedTheme } = useForcedThemeControl();
return (
<NextJSThemesProvider {...props} forcedTheme={forcedTheme || undefined}>
{children}
</NextJSThemesProvider>
);
};
function ThemeProvider({ children, ...props }: CombinedThemeProviderProps): JSX.Element {
return (
<ForcedThemeContextProvider>
<CombinedThemeProvider {...props}>
{children}
</CombinedThemeProvider>
</ForcedThemeContextProvider>
);
}
export {
ThemeProvider,
LightTheme,
DarkTheme
} |
I was facing the same problem. Creating route segments would be enough, but I wanted to avoid this as I also had only one page with forced theme. The solution I came up with was the following:
|
Isn't In this case, your local wrapper is affecting the whole website
This code will make the dark theme on one page but will set the light theme for other pages even if the dark theme is selected there. |
That was my case, but I suppose you can set |
Open two pages, one with the light theme and one with your implementation of forced dark. Also selecting any theme on the main website theme selector will change the dark theme page. |
You're right. Sorry, I just covered my very narrow use-case. What I ended up doing, however, is what is described here. That works fine with Tailwind because I can set the dark specifier on the inner component. It works correctly in multiple tabs as well. However, if it's important for you, this won't change |
I solved this in a hacky way: using "use client"
import { usePathname } from 'next/navigation'
import { ThemeProvider } from 'next-themes'
export const Providers = (props) => {
const pathname = usePathname();
const forcedThemeFromPathname = pathname === "/dark-only" ? "dark" : undefined;
return (
<ThemeProvider forcedTheme={forcedThemeFromPathname}>
{props.children}
</ThemeProvider>
)
} I'd like to find a better solution but so far I've got nothing. We need a way to pass information up from a |
Anybody found a better way to do this yet? |
here is what i did (suggestions & improvements are welcome)
|
@rohitDalalStrique that is basically what I ended up doing (as per @pacocoursey guidance above) but it is pretty damn hacky and dynamic paths become problematic. Would be great to find a better solution. |
Another solution is to put the For example: given that I would like light/dark theme for the whole website ( ├── curriculum-vitae
│ ├── layout.tsx
│ └── page.tsx
├── layout.tsx
├── (main)
│ ├── blog
│ │ ├── page.tsx
│ │ └── [slug]
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx With import { ThemeProvider } from "next-themes"
const MainLayout: React.FC = () => {
return <ThemeProvider attribute="class">{children}</ThemeProvider>
}
export default MainLayout With import { ThemeProvider } from "next-themes"
const CurriculumVitaeLayout: React.FC = () => {
return <ThemeProvider attribute="class" forcedTheme="light">{children}</ThemeProvider>
}
export default CurriculumVitaeLayout This effectively force light theme mode under |
has someone found a better way? I am using the app router but the solutions work but like some say, are a bit hacky |
How can I properly force a specific theme for a page using the app router?
I've looked at the example in the README, but it only cover the case for pages.
The text was updated successfully, but these errors were encountered: