Modify your tailwind.css fileAdd :root[class~="dark"]
to your tailwind.css file. This will allow you to use the dark
class on your html element to apply dark mode styles.
app/tailwind.css . dark ,
: root [ class ~= "dark" ] {
... ;
}
Install remix-themesStart by installing remix-themes
:
npm install remix-themes
Copy Create a session storage and theme session resolverapp/sessions.server.tsx import { createThemeSessionResolver } from "remix-themes"
// You can default to 'development' if process.env.NODE_ENV is not set
const isProduction = process . env . NODE_ENV === "production"
const sessionStorage = createCookieSessionStorage ({
cookie : {
name : "theme" ,
path : "/" ,
httpOnly : true,
sameSite : "lax" ,
secrets : [ "s3cr3t" ],
// Set domain and secure only if in production
...( isProduction
? { domain : "your-production-domain.com" , secure : true }
: {}),
},
})
export const themeSessionResolver = createThemeSessionResolver ( sessionStorage )
Set up Remix ThemesAdd the ThemeProvider
to your root layout.
app/root.tsx import clsx from "clsx"
import { PreventFlashOnWrongTheme , ThemeProvider , useTheme } from "remix-themes"
import { themeSessionResolver } from "./sessions.server"
// Return the theme from the session storage using the loader
export async function loader ({ request }: LoaderFunctionArgs ) {
const { getTheme } = await themeSessionResolver ( request )
return {
theme : getTheme (),
}
}
// Wrap your app with ThemeProvider.
// `specifiedTheme` is the stored theme in the session storage.
// `themeAction` is the action name that's used to change the theme in the session storage.
export default function AppWithProviders () {
const data = useLoaderData <typeof loader >()
return (
< ThemeProvider specifiedTheme ={ data . theme } themeAction = "/action/set-theme" >
< App />
</ ThemeProvider >
)
}
export function App () {
const data = useLoaderData <typeof loader >()
const [ theme ] = useTheme ()
return (
< html lang = "en" className ={ clsx ( theme )}>
< head >
< meta charSet = "utf-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1" />
< Meta />
< PreventFlashOnWrongTheme ssrTheme ={ Boolean ( data . theme )} />
< Links />
</ head >
< body >
< Outlet />
< ScrollRestoration />
< Scripts />
< LiveReload />
</ body >
</ html >
)
}
Add an action routeCreate a file in /routes/action.set-theme.ts
. Ensure that you pass the filename to the ThemeProvider component. This route it's used to store the preferred theme in the session storage when the user changes it.
app/routes/action.set-theme.ts import { createThemeAction } from "remix-themes"
import { themeSessionResolver } from "./sessions.server"
export const action = createThemeAction ( themeSessionResolver )
Add a mode toggleExample 1 using Dropdown Menu
in this example, we will use a Dropdown Menu
component to toggle between light, dark mode.
components/mode-toggle.tsx import { Moon , Sun } from "lucide-react"
@ @ -132,7 +134,7 @ @ import {
} from "./ui/dropdown-menu"
export function ModeToggle () {
const [ setTheme ] = useTheme ()
return (
< DropdownMenu >
@@ -156,4 +158,41 @@ export function ModeToggle() {
}
Example 2 using Toggle Group
in this example, we will use a Toggle Group
component to toggle between light, dark mode. also, we can change scale
of this for smaller view.
components/mode-toggle.tsx import { Moon , Sun } from "lucide-react"
import { Theme , useTheme } from "remix-themes"
import {
ToggleGroup ,
ToggleGroupItem
} from "~/components/ui/toggle-group"
export function ModeToggle () {
const [ setTheme ] = useTheme ()
return (
< ToggleGroup type = "single" className = "border p-1 rounded-full" >
< ToggleGroupItem
value = "Light"
className = "rounded-full"
onClick ={() => setTheme ( Theme . LIGHT )}
>
< Sun className = "h-5 w-5" />
</ ToggleGroupItem >
< ToggleGroupItem
value = "Dark"
className = "rounded-full"
onClick ={() => setTheme ( Theme . DARK )}
>
< Moon className = "h-5 w-5" />
</ ToggleGroupItem >
</ ToggleGroup >
)
}
Place a mode toggle on your site to toggle between light and dark mode.
components/mode-toggle.tsx import { Moon , Sun } from "lucide-react"
import { Theme , useTheme } from "remix-themes"
import { Button } from "./ui/button"
import {
DropdownMenu ,
DropdownMenuContent ,
DropdownMenuItem ,
DropdownMenuTrigger ,
} from "./ui/dropdown-menu"
export function ModeToggle () {
const [, setTheme ] = useTheme ()
return (
< DropdownMenu >
< DropdownMenuTrigger asChild >
< Button variant = "ghost" size = "icon" >
< Sun className = "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
< Moon className = "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
< span className = "sr-only" > Toggle theme </ span >
</ Button >
</ DropdownMenuTrigger >
< DropdownMenuContent align = "end" >
< DropdownMenuItem onClick ={() => setTheme ( Theme . LIGHT )}>
Light
</ DropdownMenuItem >
< DropdownMenuItem onClick ={() => setTheme ( Theme . DARK )}>
Dark
</ DropdownMenuItem >
</ DropdownMenuContent >
</ DropdownMenu >
)
}