Skip to Content

Built by moveinready.casa  and the community

Theming

Theming is hard to get right in React Native. This is the approach we recommend.

There are three files needed for theming:

globals.css

This is where the themes are stored. Each theme should be stored in a React Native supported color format . By default we use the shadcn/ui neutral theme. This file should be kept in the app directory.

app/globals.css
@tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --radius: 0.625rem; --background: #ffffff; --foreground: #252525; --card: #ffffff; --card-foreground: #252525; --popover: #ffffff; --popover-foreground: #252525; --primary: #343434; --primary-foreground: #fbfbfb; --secondary: #f7f7f7; --secondary-foreground: #343434; --success: #22c55e; --warning: #eab308; --muted: #f7f7f7; --muted-foreground: #8e8e8e; --accent: #f7f7f7; --accent-foreground: #343434; --destructive: #ef4444; --border: #ebebeb; --input: #ebebeb; --ring: #b5b5b5; --chart-1: #f97316; --chart-2: #06b6d4; --chart-3: #3b82f6; --chart-4: #84cc16; --chart-5: #f59e0b; --sidebar: #fbfbfb; --sidebar-foreground: #252525; --sidebar-primary: #343434; --sidebar-primary-foreground: #fbfbfb; --sidebar-accent: #f7f7f7; --sidebar-accent-foreground: #343434; --sidebar-border: #ebebeb; --sidebar-ring: #b5b5b5; } .dark:root { --background: #252525; --foreground: #fbfbfb; --card: #343434; --card-foreground: #fbfbfb; --popover: #444444; --popover-foreground: #fbfbfb; --primary: #ebebeb; --primary-foreground: #343434; --secondary: #444444; --secondary-foreground: #fbfbfb; --muted: #444444; --muted-foreground: #b5b5b5; --accent: #5f5f5f; --accent-foreground: #fbfbfb; --destructive: #dc2626; --success: #16a34a; --warning: #ca8a04; --border: rgba(255, 255, 255, 0.1); --input: rgba(255, 255, 255, 0.15); --ring: #8e8e8e; --chart-1: #8b5cf6; --chart-2: #10b981; --chart-3: #f59e0b; --chart-4: #ec4899; --chart-5: #dc2626; --sidebar: #343434; --sidebar-foreground: #fbfbfb; --sidebar-primary: #8b5cf6; --sidebar-primary-foreground: #fbfbfb; --sidebar-accent: #444444; --sidebar-accent-foreground: #fbfbfb; --sidebar-border: rgba(255, 255, 255, 0.1); --sidebar-ring: #707070; } }

You can use any of the shadcn/ui themes. See the shadcn/themes  for a full list. You will need to convert them from oklch to a supported color format.

Note

You must add :root to the .dark class. .dark:root will not work.

You can also add your own themes. They must follow this format:

app/globals.css
/* Tailwind imports, any warnings are safe to ignore */ @tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { /*Light and base variables. Anything that is not defined on dark mode will not be overridden.*/ } .dark:root { /* Dark only variables */ } } /* You must define the following variables. Below are the default values: --radius: 0.625rem; --background: #ffffff; --foreground: #252525; --card: #ffffff; --card-foreground: #252525; --popover: #ffffff; --popover-foreground: #252525; --primary: #343434; --primary-foreground: #fbfbfb; --secondary: #f7f7f7; --secondary-foreground: #343434; --success: #22c55e; --warning: #eab308; --muted: #f7f7f7; --muted-foreground: #8e8e8e; --accent: #f7f7f7; --accent-foreground: #343434; --destructive: #ef4444; --border: #ebebeb; --input: #ebebeb; --ring: #b5b5b5; --chart-1: #f97316; --chart-2: #06b6d4; --chart-3: #3b82f6; --chart-4: #84cc16; --chart-5: #f59e0b; --sidebar: #fbfbfb; --sidebar-foreground: #252525; --sidebar-primary: #343434; --sidebar-primary-foreground: #fbfbfb; --sidebar-accent: #f7f7f7; --sidebar-accent-foreground: #343434; --sidebar-border: #ebebeb; --sidebar-ring: #b5b5b5; */

tailwind.config.js

This is where you make your theme variables accessible to NativeWind. You should use the configuration below most of the time:

tailwind.config.js
/** @type {import('tailwindcss').Config} */ export default { darkMode: "class", content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"], presets: [require("nativewind/preset")], theme: { extend: { colors: { border: "var(--border)", input: "var(--input)", ring: "var(--ring)", background: "var(--background)", foreground: "var(--foreground)", primary: { DEFAULT: "var(--primary)", foreground: "var(--primary-foreground)", }, secondary: { DEFAULT: "var(--secondary)", foreground: "var(--secondary-foreground)", }, destructive: { DEFAULT: "var(--destructive)", foreground: "var(--destructive-foreground)", }, success: { DEFAULT: "var(--success)", foreground: "var(--success-foreground)", }, warning: { DEFAULT: "var(--warning)", foreground: "var(--warning-foreground)", }, muted: { DEFAULT: "var(--muted)", foreground: "var(--muted-foreground)", }, accent: { DEFAULT: "var(--accent)", foreground: "var(--accent-foreground)", }, popover: { DEFAULT: "var(--popover)", foreground: "var(--popover-foreground)", }, card: { DEFAULT: "var(--card)", foreground: "var(--card-foreground)", }, sidebar: { DEFAULT: "var(--sidebar-background)", foreground: "var(--sidebar-foreground)", primary: "var(--sidebar-primary)", "primary-foreground": "var(--sidebar-primary-foreground)", accent: "var(--sidebar-accent)", "accent-foreground": "var(--sidebar-accent-foreground)", border: "var(--sidebar-border)", ring: "var(--sidebar-ring)", }, }, borderRadius: { xl: "calc(var(--radius) + 4px)", lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", full: "100%", }, }, }, plugins: [], };
Note

Make sure to update the content property to your tsx paths.

At this point you might be wondering how dark mode is applied. Using the darkMode: "class" option will automatically apply the dark class to the root view or body element.

Web usage: You may run into an issue where dark mode is not applied or is broken on web. In that case you can add the following to your root layout:

// Handle dark mode class for web useEffect(() => { if (Platform.OS === "web") { // Type assertion for web platform where document is available const doc = (globalThis as any).document; if (doc) { const root = doc.documentElement; if (colorScheme === "dark") { root.classList.add("dark"); } else { root.classList.remove("dark"); } } } }, [colorScheme]);

theme.ts

This file does not affect your theming with tailwind variables but rather when you need to access the theme variables with JavaScript. This file should be kept in the lib/utils directory. Keep it in sync with globals.css.

lib/utils/theme.ts
import {useColorScheme} from "react-native"; export const theme = { light: { radius: "0.625rem", background: "#ffffff", foreground: "#252525", card: "#ffffff", cardForeground: "#252525", popover: "#ffffff", popoverForeground: "#252525", primary: "#343434", primaryForeground: "#fbfbfb", secondary: "#f7f7f7", secondaryForeground: "#343434", success: "#22c55e", warning: "#eab308", muted: "#f7f7f7", mutedForeground: "#8e8e8e", accent: "#f7f7f7", accentForeground: "#343434", destructive: "#ef4444", border: "#ebebeb", input: "#ebebeb", ring: "#b5b5b5", chart1: "#f97316", chart2: "#06b6d4", chart3: "#3b82f6", chart4: "#84cc16", chart5: "#f59e0b", sidebar: "#fbfbfb", sidebarForeground: "#252525", sidebarPrimary: "#343434", sidebarPrimaryForeground: "#fbfbfb", sidebarAccent: "#f7f7f7", sidebarAccentForeground: "#343434", sidebarBorder: "#ebebeb", sidebarRing: "#b5b5b5", }, dark: { background: "#252525", foreground: "#fbfbfb", card: "#343434", cardForeground: "#fbfbfb", popover: "#444444", popoverForeground: "#fbfbfb", primary: "#ebebeb", primaryForeground: "#343434", secondary: "#444444", secondaryForeground: "#fbfbfb", muted: "#444444", mutedForeground: "#b5b5b5", accent: "#5f5f5f", accentForeground: "#fbfbfb", destructive: "#dc2626", success: "#16a34a", warning: "#ca8a04", border: "rgba(255, 255, 255, 0.1)", input: "rgba(255, 255, 255, 0.15)", ring: "#8e8e8e", chart1: "#8b5cf6", chart2: "#10b981", chart3: "#f59e0b", chart4: "#ec4899", chart5: "#dc2626", sidebar: "#343434", sidebarForeground: "#fbfbfb", sidebarPrimary: "#8b5cf6", sidebarPrimaryForeground: "#fbfbfb", sidebarAccent: "#444444", sidebarAccentForeground: "#fbfbfb", sidebarBorder: "rgba(255, 255, 255, 0.1)", sidebarRing: "#707070", }, }; export const useTheme = () => { const colorScheme = useColorScheme(); return theme[colorScheme || "light"]; }; // Suppress SVGElement error class SVGElement {} // @ts-ignore globalThis.SVGElement = SVGElement;
Note

At the bottom of the file you will see a global SVGElement class. This is a workaround to fix an issue with react-aria  running on native and it must be kept.

To update the file with your theme we recommend the following prompt for LLMs:

Please update the lib/utils/theme.ts file with the themes found in app/globals.css. Create 2 nested objects, dark and light. Define the radius variable in both light and dark mode. Do not include -- in the variable names. Use camelCase for the variable names.

Additional Resources

shadcn-native

Built by moveinready.casa and the community