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" ] {
...;
}
Copy 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)
Copy 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 >
)
}
Copy 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)
Copy 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() {
}
Copy 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 >
)
}
Copy 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 >
)
}
Copy