DEV Community

Amina Tariq
Amina Tariq

Posted on

Context vs Redux for Theme Management in React Native: Which One Should You Choose?

Dark mode is no longer a “nice to have” it’s a user expectation. Whether you’re building a consumer app or an enterprise solution, offering both light and dark themes (with system auto-detection and manual override) significantly improves user experience.
But here comes the question: how do you manage theme state across your app?

The two most common approaches in React Native are:

  1. React Context (with useColorScheme)
  2. Redux (or another global state library)

Let’s break down both approaches, their pros, cons, and when to use each.

1. Theme with React Context
React Context combined with useColorScheme is the lightweight and native-friendly solution.

How It Works

theme.ts

export const lightTheme = {
  background: "#FFFFFF",
  text: "#000000",
};

export const darkTheme = {
  background: "#000000",
  text: "#FFFFFF",
};
Enter fullscreen mode Exit fullscreen mode

ThemeProvider.tsx

import React, { createContext, useContext } from "react";
import { useColorScheme } from "react-native";
import { lightTheme, darkTheme } from "./theme";

const ThemeContext = createContext(lightTheme);

export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
  const scheme = useColorScheme();
  const theme = scheme === "dark" ? darkTheme : lightTheme;

  return (
    <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);
Enter fullscreen mode Exit fullscreen mode

Usage in a screen

import React from "react";
import { View, Text } from "react-native";
import { useTheme } from "../ThemeProvider";

export default function HomeScreen() {
  const theme = useTheme();

  return (
    <View style={{ flex: 1, backgroundColor: theme.background }}>
      <Text style={{ color: theme.text }}>Hello, Context Theme!</Text>
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

Advantages

  • Lightweight: No external library required.
  • Simple: Perfect for small-to-medium apps.
  • Auto-detect: Easily integrates with system settings using useColorScheme.

Drawbacks

  • Limited scaling: Managing overrides (for example, theme plus language plus settings) can get messy.
  • Persistence required: If you want user-selected theme (not just system), you’ll need AsyncStorage or a similar solution.
  • Re-renders: Context can trigger unnecessary re-renders if overused. __________________________________________________________________________

2. Theme with Redux
Redux takes a state-first approach, making it a better fit for larger apps where preferences are part of global state.

How It Works

themeSlice.ts

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

type ThemeState = {
  mode: "light" | "dark" | "system";
};

const initialState: ThemeState = {
  mode: "system",
};

const themeSlice = createSlice({
  name: "theme",
  initialState,
  reducers: {
    setTheme: (state, action: PayloadAction<"light" | "dark" | "system">) => {
      state.mode = action.payload;
    },
  },
});

export const { setTheme } = themeSlice.actions;
export default themeSlice.reducer;

Enter fullscreen mode Exit fullscreen mode

useAppTheme.ts

import { useColorScheme } from "react-native";
import { useSelector } from "react-redux";
import { RootState } from "./store";
import { lightTheme, darkTheme } from "./theme";

export const useAppTheme = () => {
  const systemScheme = useColorScheme(); // 'light' or 'dark'
  const mode = useSelector((state: RootState) => state.theme.mode);

  const scheme = mode === "system" ? systemScheme : mode;

  return scheme === "dark" ? darkTheme : lightTheme;
};

Enter fullscreen mode Exit fullscreen mode

Usage in a screen

import React from "react";
import { View, Text, Button } from "react-native";
import { useAppTheme } from "../useAppTheme";
import { useDispatch } from "react-redux";
import { setTheme } from "../themeSlice";

export default function SettingsScreen() {
  const theme = useAppTheme();
  const dispatch = useDispatch();

  return (
    <View style={{ flex: 1, backgroundColor: theme.background }}>
      <Text style={{ color: theme.text }}>Theme Settings</Text>
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

Advantages

  • Centralized control: Everything runs through Redux, making it easy to debug.
  • Persistence: Combine with redux-persist for long-term storage.
  • Scales better: Useful if you’re already using Redux for authentication, settings, or language preferences.

Drawbacks

  • Overhead: If you don’t already use Redux, it’s overkill just for theme.
  • Boilerplate: Slices, actions, and selectors add more code.
  • Extra dependency: Adds Redux to your bundle if it’s not already there.

Recommendation
Start simple: If your app doesn’t already use Redux, stick with Context. It’s lean, straightforward, and integrates smoothly with system theme detection.

Go Redux: If your app already uses Redux (or will grow to include global state like user preferences, localization, or complex settings), it makes sense to manage theme inside Redux for consistency.

Think of it like this:
Context is a screwdriver — small, handy, lightweight.
Redux is a toolbox — heavier, but gives you everything you need for a big project.

Conclusion
Theme management in React Native doesn’t have to be complicated. Use Context when you want speed and simplicity. Use Redux when you need scalability, persistence, and centralized control.

Both are valid approaches; the real decision comes down to the size of your app and the complexity of your state.

Top comments (0)