Dark Mode
Customizing the theme in gluestack-ui v2 for different color schemes and color mode support.
Color Scheme
Using Tailwind Dark Mode
Persist Color Mode
Color Scheme
gluestack-ui provides two ways of switching the color scheme or color mode: using CSS variables and using the dark: className support with nativewind.
Using CSS Variables
With CSS variables, you can configure multiple color schemes in the config.ts file. This file contains two predefined color schemes: light and dark. It utilizes the vars functionality from nativewind to switch token values when the color mode is changed. This approach results in less code and makes configuring tokens for different color schemes easier.
Usage
Let's look at an example where we define the primary token and switch it.
- Step1:First, define the color token in your tailwind.config.js file and assign a variable value as shown below, following Tailwind's recommendation for using CSS variables in Tailwind.
// tailwind.config.jsmodule.exports = {theme: {extend: {colors: {primary: 'rgb(var(--color-primary)/<alpha-value>)'}}}}
- Step2:Now, define the values of that CSS variable for the light and dark color schemes in the config.ts file as shown below. Reference.
// config.tsexport const config = {light: vars({'--color-primary': '51 51 51',}),dark: vars({'--color-primary': '240 240 240',})}
- Step3:Pass the color mode to the GluestackUIProvider using the mode prop and use the tokens inside your code. modeprop accepts three values: system, light and dark.
- system: It uses the system color mode.
- light: It uses the light color mode.
- dark: It uses the dark color mode.
// App.tsximport { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider";import { Box } from "@/components/ui/box";import { Button, ButtonText } from "@/components/ui/Button";import { useState } from 'react';export default function App() {const [colorMode, setColorMode] = useState<"light" | "dark">("light");return (<GluestackUIProvider mode={colorMode}><Box className="bg-primary flex-1"><ButtononPress={() => {setColorMode(colorMode === "light" ? "dark" : "light");}}><ButtonText>Toggle color mode</ButtonText></Button></Box></GluestackUIProvider>)}
Using Tailwind Dark Mode
gluestack-ui also supports Tailwind's default dark: concept. Let's see how to use it with an example.
// App.tsximport { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider";import { Box } from "@/components/ui/box";import { Button, ButtonText } from "@/components/ui/Button";import { useState } from 'react';export default function App() {const [colorMode, setColorMode] = useState<"light" | "dark">("light");return (<GluestackUIProvider mode={colorMode}><Box className="bg-white dark:bg-black flex-1"><ButtononPress={() => {setColorMode(colorMode === "light" ? "dark" : "light");}}><ButtonText>Toggle color mode</ButtonText></Button></Box></GluestackUIProvider>)}
In the above example, we switch the background color of our Box component in dark mode using the dark: syntax. To use dark mode, we need to change the darkMode strategy to "class" for the web and "media" for native devices in the tailwind.config.js file. You can achieve this by setting the DARK_MODE environment variable as shown below.
// tailwind.config.jsmodule.exports = {darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : 'media',// rest of the config}
After this, we need to update our scripts in the package.json file as shown below:
{"scripts": {"android": "DARK_MODE=media expo start --android","ios": "DARK_MODE=media expo start --ios","web": "DARK_MODE=class expo start --web"}}
For Next.js projects, you can directly set the darkMode strategy to "class" without needing to change the scripts.
Important Note
Note: This is a temporary solution until we fix the issue with nativewind for the darkMode:"class" strategy.
Persist Color Mode
Native
Web
Step 1: Install the following dependencies:
npm i @react-native-async-storage/async-storage
We will use the @react-native-async-storage/async-storage package to store the color mode in the device's local storage. You can also use other storage solutions.
Step 2: Create a new file ThemeProvider.tsx at components/ui/ThemeProvider.
// components/ui/ThemeProvider/ThemeProvider.tsx"use client"import React, { createContext, useState, useEffect, useContext } from "react"import AsyncStorage from "@react-native-async-storage/async-storage"type Theme = "light" | "dark"interface ThemeContextType {theme: ThemetoggleTheme: () => void}export const ThemeContext = createContext<ThemeContextType | undefined>(undefined)export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {const [theme, setTheme] = useState<Theme>("light")useEffect(() => {;(async () => {const savedTheme = (await AsyncStorage.getItem("theme")) as| Theme| "light"if (savedTheme) {setTheme(savedTheme)AsyncStorage.setItem("theme", savedTheme)}})()}, [])const toggleTheme = () => {const newTheme = theme === "light" ? "dark" : "light"setTheme(newTheme)AsyncStorage.setItem("theme", newTheme)}return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>)}export const useTheme = () => {const context = useContext(ThemeContext)if (context === undefined) {throw new Error("useTheme must be used within a ThemeProvider")}return context}
Step 3: Update your app's root to utilize the ThemeProvider component.
Please refer to the Tailwind CSS dark mode documentation for more information and core concepts of dark mode.