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.
  1. Step
    1
    :
    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.js
module.exports = {
theme: {
extend: {
colors: {
primary: 'rgb(var(--color-primary)/<alpha-value>)'
}
}
}
}
  1. Step
    2
    :
    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.ts
export const config = {
light: vars({
'--color-primary': '51 51 51',
}),
dark: vars({
'--color-primary': '240 240 240',
})
}
  1. Step
    3
    :
    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.tsx
import { 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">
<Button
onPress={() => {
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.tsx
import { 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">
<Button
onPress={() => {
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.js
module.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

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: Theme
toggleTheme: () => 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.