Sigma Stack
2 min read

Light & Dark Mode in React Native with Design Tokens

By Arslan Shaukat

React NativeUITheming

Hardcoded hex values and padding: 16 scattered across files are how an app slowly drifts out of consistency — and why adding dark mode later feels impossible. The fix is to drive everything from design tokens: one source of truth for color, spacing, and radius. Here's how.

Define the tokens

Start with two palettes and a few scales:

export const light = {
  bg: "#F6F6FB",
  surface: "#FFFFFF",
  text: "#15151F",
  accent: "#5B57E6",
};

export const dark = {
  bg: "#0A0A14",
  surface: "#12121F",
  text: "#ECECF5",
  accent: "#7C7AF5",
};

export const space = (n: number) => n * 8;
export const radius = { md: 12, lg: 18 };

Every color in the app comes from one of these objects. There are no raw hex values in components, ever.

Expose the active theme

Put the current palette behind a context so components read tokens, not values:

const ThemeCtx = createContext(dark);
export const useTheme = () => useContext(ThemeCtx);

export function ThemeProvider({ children }: { children: ReactNode }) {
  const scheme = useColorScheme(); // "light" | "dark"
  const value = scheme === "light" ? light : dark;
  return <ThemeCtx.Provider value={value}>{children}</ThemeCtx.Provider>;
}

useColorScheme from React Native follows the device setting automatically, so the app respects the user's OS preference out of the box.

Use tokens in components

function Card({ children }: { children: ReactNode }) {
  const t = useTheme();
  return (
    <View style={{ backgroundColor: t.surface, borderRadius: radius.lg, padding: space(2) }}>
      {children}
    </View>
  );
}

Because the component reads t.surface rather than a literal color, it's correct in both themes with zero extra code.

Why this scales

  • A redesign is a token change, not a find-and-replace across fifty files.
  • Dark mode is free — it's just the second palette.
  • Consistency is enforced because there's nowhere to put a one-off color.

This is the "no magic numbers" rule, and it's baked into both my React Native Starter and the AI Chat App UI Kit — every color and spacing value flows from tokens, so theming is a solved problem from the first screen.

Skip the boilerplate

Production-ready React Native starters and UI kits — buy once, clone, and start on the feature that matters.

Browse the templates

More

Keep reading