W
Wasp2d ago
Dada

useColorMode hook

im using the the userColorMode hook inside the App.tsx : app/src/client/App.tsx , using it to change the An Design ConfigProvider theme to dark mode when the colorMode state change. but the problem is App.tsx doesn't render when the colorMode changes , is that normal or am i doing something wrong ?
16 Replies
kapa.ai
kapa.ai2d ago
Hi @Dada. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information in the knowledge sources, I can help explain what might be happening with your useColorMode hook in App.tsx. The issue you're experiencing is likely related to how Wasp handles the root component structure. In Wasp, the App.tsx component is typically used as a wrapper for all other components in your application. However, when the color mode changes, your App component isn't re-rendering as expected. This could be because in Wasp, you should ideally handle theme providers in a root component that's specifically defined for this purpose. Wasp provides a way to define a root component that wraps your entire application using the rootComponent field in the client configuration. Here's how you can properly set up a theme provider in Wasp:
app MyApp {
title: "My app",
// ...
client: {
rootComponent: import Root from "@src/Root.tsx",
}
}
app MyApp {
title: "My app",
// ...
client: {
rootComponent: import Root from "@src/Root.tsx",
}
}
Then in your Root.tsx file:
import { Provider } from 'your-theme-provider'

export default function Root({ children }: { children: React.ReactNode }) {
return (
<Provider>
{children}
</Provider>
)
}
import { Provider } from 'your-theme-provider'

export default function Root({ children }: { children: React.ReactNode }) {
return (
<Provider>
{children}
</Provider>
)
}
This approach is recommended in the Wasp documentation for setting up providers. The reason your current approach might not be working is that the App component in Wasp has a specific role in the component hierarchy, and theme changes might not trigger re-renders at that level properly. Also, if you're using Tailwind for dark mode, note that there have been some issues reported with Tailwind configuration in Wasp projects, where you might need to restart the application after making configuration changes for them to take effect, as mentioned in this GitHub issue. I hope this helps! Let me know if you need more specific guidance on implementing theme switching in your Wasp application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Dada, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Dada
DadaOP2d ago
@Wasp Team any idea guys ?
toticozeni
toticozeni2d ago
Hey @Dada, what UI library are you using? The kapa.ai gave a good hint. Global application providers should go inside of the root component. This is so that their state isn't re-initialized each time you navigate to a different page. From what I can see useColorMode is used in Charka, do correct me if im wrong. In that case for your Root.tsx file you would have:
import { ColorModeProvider } from "@/components/ui/color-mode"
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"

export default function Root({ children }: { children: React.ReactNode }) {
return (
<ChakraProvider value={defaultSystem}>
<ColorModeProvider>{children}</ColorModeProvider>
</ChakraProvider>
)
}
import { ColorModeProvider } from "@/components/ui/color-mode"
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"

export default function Root({ children }: { children: React.ReactNode }) {
return (
<ChakraProvider value={defaultSystem}>
<ColorModeProvider>{children}</ColorModeProvider>
</ChakraProvider>
)
}
but also make sure that root component is linked in the wasp.config
app MyApp {
title: "My app",
// ...
client: {
rootComponent: import Root from "@src/Root.tsx",
}
}
app MyApp {
title: "My app",
// ...
client: {
rootComponent: import Root from "@src/Root.tsx",
}
}
If the issue still persists do tell me.
Dada
DadaOP2d ago
hey @franjo , thanks for your reply. The UI library im using is Ant Design , here is my setup : - app/src/client/App.tsx : root application. - app/src/client/hooks/userColorMode.tsx : react hook to change the color mode. - app/src/client/components/DarkModeSwitcher.tsx : the switch button that changes the theme dark/light. - main.wasp .
No description
No description
No description
No description
Vinny (@Wasp)
Vinny (@Wasp)2d ago
Hm maybe mixing ant design with the current tailwind setup is causing the problem. You'll see in tailwind.config.cjs that we're defining the mode by class. So open saas by default doesn't use a Provider in the root component, rather you just define what your dark mode styles should be like this: className: text-black dark:text-white`
Vinny (@Wasp)
Vinny (@Wasp)2d ago
GitHub
open-saas/template/app/tailwind.config.cjs at main · wasp-lang/ope...
A free, open-source SaaS app starter for React & Node.js with superpowers. Full-featured. Community-driven. - wasp-lang/open-saas
Dada
DadaOP2d ago
yes i saw that , i was hoping there is a way to use the Ant Design built-in Dark Mode , without having to manually add the classes for every component i use :sadboi:
MEE6
MEE62d ago
Wohooo @Dada, you just became a Waspeteer level 2!
Vinny (@Wasp)
Vinny (@Wasp)2d ago
you'll have to dig into ant design and see for yourself, otherwise just stick with the current set up. Why do you want to use ant design?
toticozeni
toticozeni2d ago
Hey, each hook instance has it's own state unless they are using same one via e.g. context. The useColorMode hook state in DarkModeSwitcher.tsx is not the same one used in App.tsx
Dada
DadaOP2d ago
its the UI Library i always use , provides all the components i need and im kinda used to it thats all , if you recommend another UI Library thats compatible with your setup i'll give it a try
toticozeni
toticozeni2d ago
Also, if it doesn't export any jsx components, useColorMode should be preferably a .ts file instead of .tsx. I'll write more detailed response about the hook now.
Vinny (@Wasp)
Vinny (@Wasp)2d ago
maybe ShadCN is a good option. Here's how to set it up in Wasp: https://gist.github.com/infomiho/b35e9366e16913949e13eaba0538f553
toticozeni
toticozeni2d ago
If you want to keep the libraries you could unify the state via context: ColorModeContext.tsx
import React, { createContext, useContext } from 'react';
import useLocalStorage from './useLocalStorage';

type ColorMode = 'dark' | 'light';
const ColorModeContext = createContext<{
colorMode: ColorMode;
setColorMode: (mode: ColorMode) => void;
} | undefined>(undefined);

export const ColorModeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [colorMode, setColorMode] = useLocalStorage('color-theme', 'light');

useEffect(() => {
const className = 'dark';
const bodyClass = window.document.body.classList;
colorMode === 'dark'
? bodyClass.add(className)
: bodyClass.remove(className);
}, [colorMode]);

return (
<ColorModeContext.Provider value={{ colorMode, setColorMode }}>
{children}
</ColorModeContext.Provider>
);
};

export const useColorMode = () => {
const context = useContext(ColorModeContext);
if (!context) {
throw new Error('useColorMode must be used within a ColorModeProvider');
}
return context;
};
import React, { createContext, useContext } from 'react';
import useLocalStorage from './useLocalStorage';

type ColorMode = 'dark' | 'light';
const ColorModeContext = createContext<{
colorMode: ColorMode;
setColorMode: (mode: ColorMode) => void;
} | undefined>(undefined);

export const ColorModeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [colorMode, setColorMode] = useLocalStorage('color-theme', 'light');

useEffect(() => {
const className = 'dark';
const bodyClass = window.document.body.classList;
colorMode === 'dark'
? bodyClass.add(className)
: bodyClass.remove(className);
}, [colorMode]);

return (
<ColorModeContext.Provider value={{ colorMode, setColorMode }}>
{children}
</ColorModeContext.Provider>
);
};

export const useColorMode = () => {
const context = useContext(ColorModeContext);
if (!context) {
throw new Error('useColorMode must be used within a ColorModeProvider');
}
return context;
};
Root.tsx Just create another wrapper around App.tsx, so you could use useColorMode inside of App.tsx. The alternative is to define useLocalStorage stuff inside of App.tsx and pass it to the provider instead of defining it inside of ColorModeContext.
return (
<ColorModeProvider>
<App />
</ColorModeProvider>
);
return (
<ColorModeProvider>
<App />
</ColorModeProvider>
);
The DarkModeSwitcher.tsx should stay the same and work. I didn't test this code so there might be little mistakes, but the logic should work. @Dada
Dada
DadaOP2d ago
Thank you so much @franjo i will give it a try and let you know , if it didn't work i'll just switch to ShadCN is Vinny suggested , appreciate your help guys Thank you again @franjo it worked ✅
toticozeni
toticozeni2d ago
Woo hoo 🔥

Did you find this page helpful?