Setup Theme Provider
Implement light/dark mode theming in your Expo app using React Navigation. This guide covers configuring the ThemeProvider with your custom color palette.
Step 1: Install Dependencies
You need the core React Navigation package and its peer dependencies if you haven't already installed them.
npx expo install @react-navigation/native react-native-screens react-native-safe-area-context
Step 2: Define Your Color Palette
First, ensure you have a file (e.g., constants/Colors.ts
) defining your color variables for both light
and dark
themes.:
export const Colors = {
light: {
// Brand Colors
PrimaryNormal: "#E3A547",
// ... other light theme colors
Neutral900: "#000000",
bgColor: "#F2F2F2",
// ...
},
dark: {
// Brand Colors
PrimaryNormal: "#E3A547",
// ... other dark theme colors
Neutral900: "#FFFFFF",
bgColor: "#1C1C1E",
// ...
},
};
Step 3: Configure Navigation Themes
Import the default themes from React Navigation and your custom colors. Merge them to create theme objects that include your palette.
import { DarkTheme, DefaultTheme } from "@react-navigation/native";
import { Colors } from "@/constants/Colors"; // Adjust path
// Merge default dark theme with your custom dark colors
export const CustomDarkTheme = {
...DarkTheme,
colors: {
...DarkTheme.colors,
...Colors.dark,
},
};
// Merge default light theme with your custom light colors
export const CustomLightTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
...Colors.light,
},
};
Step 4: Apply the ThemeProvider
Wrap your root navigator or the entire app layout with the ThemeProvider
component. Use the useColorScheme
hook (from React Native or your own implementation) to dynamically select the correct theme object.
import { ThemeProvider } from "@react-navigation/native";
import { useColorScheme } from "react-native"; // Or your custom hook
import { CustomDarkTheme, CustomLightTheme } from "./path/to/your/themes"; // Adjust path
export default function RootLayout() {
const colorScheme = useColorScheme();
// ... other setup
return (
<ThemeProvider
value={colorScheme === "dark" ? CustomDarkTheme : CustomLightTheme}
>
{/* Your App's Navigation Stack or Main Component */}
<Stack>
{/* ... screens */}
</Stack>
</ThemeProvider>
);
}
Step 5: Apply the ThemeProvider
Import and use the useTheme hook from @react-navigation/native
within your components to access the currently active theme's colors.
import { useTheme } from '@react-navigation/native';
import { Text, View } from 'react-native';
function MyComponent() {
const { colors } = useTheme(); // Use the hook
return (
<View style={{ backgroundColor: colors.bgColor }}>
<Text style={{ color: colors.PrimaryNormal }}>
Hello Themed World!
</Text>
{/* Access any color defined in your Colors.ts */}
<Text style={{ color: colors.Neutral700 }}>
Some neutral text.
</Text>
</View>
);
}
Step 6:Enhance Type Safety (Optional)
For better autocompletion and type checking on your custom color keys, create a theme.d.ts
declaration file and augment the @react-navigation/native
module.
import { Theme } from '@react-navigation/native';
// Adjust path to your Colors definition if necessary
// It doesn't need the actual values, just the keys for type checking
import { Colors } from "@/constants/Colors";
// Get the type of keys from one of the themes (light/dark)
// Assuming both have the same keys
type CustomColorKeys = keyof typeof Colors.light;
// Create a mapped type for the colors record
// Ensures all keys from your Colors object are typed as string
type CustomColors = {
[key in CustomColorKeys]: typeof Colors.light[key] extends string[] ? string[] : string;
}
// Define extended theme type
interface ExtendedTheme extends Theme {
colors: Theme['colors'] & CustomColors;
}
// Augment the module
declare module '@react-navigation/native' {
export function useTheme(): ExtendedTheme;
// You can also augment DefaultTheme and DarkTheme if needed
// export const DefaultTheme: ExtendedTheme;
// export const DarkTheme: ExtendedTheme;
}