Implementing Light and Dark Themes in React Native
Course: React Native Training
Topic: Implementing Light and Dark Themes
Course Link: React Native Training at JustAcademy
Demo Registration: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction to Light and Dark Themes
Themes define the visual appearance of a mobile application, including background colors, text colors, buttons, cards, icons, borders, and navigation elements. A light theme typically uses light backgrounds with darker text, while a dark theme uses darker backgrounds with lighter text.
React Native applications can support both themes and allow users to switch between them. The application can follow the device's system appearance automatically or provide an in-app setting that lets users select their preferred theme.
For example, a learning application might display a white background with dark text during light mode and a dark gray background with light text during dark mode. The layout and functionality remain the same while the colors change.
1.1 What Is a Light Theme?
A light theme uses bright backgrounds, dark text, and carefully selected accent colors. It is commonly used in applications that prioritize a clean, familiar appearance and readability in well-lit environments.
- White or light gray screen backgrounds.
- Dark gray or black primary text.
- Light-colored cards and input fields.
- Dark borders or subtle shadows for separation.
- Accent colors for buttons, links, and important actions.
1.2 What Is a Dark Theme?
A dark theme uses dark backgrounds and lighter foreground elements. It can reduce the brightness of the interface in dim environments and may be preferred by users who find dark interfaces more comfortable.
- Dark gray, charcoal, or black backgrounds.
- Light-colored primary text.
- Dark surfaces for cards and dialogs.
- Muted secondary text with sufficient contrast.
- Accent colors adjusted to remain visible against dark surfaces.
1.3 What Is Automatic Theme Switching?
Automatic theme switching means the application detects the operating system's preferred color scheme and updates its interface accordingly. React Native provides the useColorScheme hook for observing changes to the device's preferred color scheme.
Reference: React Native useColorScheme Documentation.
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand light themes, dark themes, and system-based appearance.
- Use the React Native
useColorScheme hook.
- Create separate color palettes for light and dark modes.
- Apply themes to backgrounds, text, cards, buttons, and input fields.
- Implement a manual theme switcher.
- Support system, light, and dark theme preferences.
- Organize theme configuration into reusable files.
- Persist a user's theme preference between application sessions.
- Implement theme support in React Native Paper.
- Test themes for accessibility, consistency, and platform compatibility.
3. Why Are Light and Dark Themes Important?
Theme support improves personalization and allows users to select an interface that suits their environment and preferences.
3.1 Benefits
- User preference: Users can choose an appearance that suits their needs.
- Comfort: Dark interfaces may be preferable in dim environments, although comfort varies by user.
- Consistency: A centralized theme keeps colors consistent throughout the application.
- Maintainability: Updating a theme is easier when colors are defined in one place.
- Brand identity: Both themes can use a consistent set of brand colors.
- Accessibility: Thoughtful color and contrast choices can improve readability.
- Platform integration: Following the system appearance can make the application feel more integrated with the device.
3.2 Common Applications
- Social media applications.
- News and reading applications.
- Learning management systems.
- Banking and finance applications.
- Shopping applications.
- Productivity tools and dashboards.
- Messaging applications.
4. Understanding React Native Appearance APIs
React Native provides the Appearance API and the useColorScheme hook to access the preferred color scheme. For most functional components, useColorScheme is the recommended starting point because it subscribes to changes.
4.1 The useColorScheme Hook
The hook returns the preferred appearance, which can be "light", "dark", or null when the preference is unavailable.
import React from 'react';
import { Text, View, useColorScheme } from 'react-native';
export default function App() {
const colorScheme = useColorScheme();
return (
<View>
<Text>
Current scheme: {colorScheme ?? 'unknown'}
</Text>
</View>
);
}
4.2 Explanation
useColorScheme is imported from react-native.
- The hook reads the current preferred color scheme.
- The component re-renders when the observed system preference changes.
- The returned value can be used to choose theme colors and component styles.
4.3 The Appearance API
The Appearance API provides methods for reading and, where supported, overriding the application's color scheme.
import { Appearance } from 'react-native';
const currentScheme = Appearance.getColorScheme();
console.log(currentScheme);
// Restore following the system appearance:
Appearance.setColorScheme('auto');
Use the current React Native documentation for the exact supported values in your installed version. On modern versions, Appearance.setColorScheme('auto') restores following the system preference. This API changes the app's appearance behavior; it does not change the system setting for other applications.
Reference: React Native Appearance Documentation.
5. Creating a Basic Light and Dark Theme
The simplest implementation uses conditional colors based on the current color scheme. This approach is suitable for learning and small applications.
5.1 Basic Example
import React from 'react';
import {
View,
Text,
StyleSheet,
useColorScheme
} from 'react-native';
export default function App() {
const colorScheme = useColorScheme();
const isDark = colorScheme === 'dark';
return (
<View
style={[
styles.container,
{
backgroundColor: isDark ? '#121212' : '#FFFFFF'
}
]}
>
<Text
style={[
styles.title,
{
color: isDark ? '#FFFFFF' : '#222222'
}
]}
>
Welcome to My App
</Text>
<Text
style={{
color: isDark ? '#CCCCCC' : '#555555'
}}
>
Light and dark themes are supported.
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 24
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 12
}
});
5.2 How It Works
- The application reads the system color scheme with
useColorScheme.
- The variable
isDark becomes true when the scheme is dark.
- The background uses a dark color in dark mode and white in light mode.
- The title and description also change color.
- When the system appearance changes, the component updates its appearance.
5.3 Limitations of the Basic Approach
Conditional styling is simple, but repeating color conditions in every component can make a large application difficult to maintain. A better solution is to define a centralized theme object and reuse it across screens.
6. Creating Centralized Theme Configuration
A centralized theme configuration stores the colors and other visual values used throughout an application. Instead of defining colors repeatedly, components read them from the active theme.
6.1 Suggested Folder Structure
MyApp/
├── App.tsx
└── src/
├── theme/
│ ├── colors.ts
│ ├── lightTheme.ts
│ └── darkTheme.ts
├── components/
│ ├── ThemeButton.tsx
│ └── ProfileCard.tsx
└── screens/
└── HomeScreen.tsx
6.2 Defining the Theme Colors
Create a file named src/theme/colors.ts.
export const lightTheme = {
mode: 'light' as const,
colors: {
background: '#FFFFFF',
surface: '#F5F5F5',
primary: '#2563EB',
text: '#1F2937',
secondaryText: '#6B7280',
border: '#D1D5DB',
card: '#FFFFFF',
success: '#15803D',
danger: '#B91C1C'
}
};
export const darkTheme = {
mode: 'dark' as const,
colors: {
background: '#121212',
surface: '#1E1E1E',
primary: '#8AB4F8',
text: '#F9FAFB',
secondaryText: '#C0C4CC',
border: '#3F3F46',
card: '#242424',
success: '#86EFAC',
danger: '#FCA5A5'
}
};
export type AppTheme =
| typeof lightTheme
| typeof darkTheme;
6.3 Understanding the Theme Properties
| Property |
Purpose |
| background |
Main screen background. |
| surface |
Background for secondary sections, panels, or controls. |
| primary |
Brand color for important actions and highlights. |
| text |
Primary text color. |
| secondaryText |
Supporting text, descriptions, and captions. |
| border |
Divider and outline color. |
| card |
Background color for cards and grouped content. |
| success |
Positive status messages. |
| danger |
Error or destructive-action indicators. |
Keeping these properties consistent allows developers to switch themes without rewriting each component's styles.
7. Applying the Theme to Reusable Components
Once theme objects exist, pass the active theme to reusable components or expose it through React Context. For a small example, passing a theme as a prop is straightforward.
7.1 Creating a Themed Card
import React from 'react';
import {
View,
Text,
StyleSheet
} from 'react-native';
export default function ProfileCard({ theme }) {
return (
<View
style={[
styles.card,
{
backgroundColor: theme.colors.card,
borderColor: theme.colors.border
}
]}
>
<Text
style={[
styles.title,
{ color: theme.colors.text }
]}
>
User Profile
</Text>
<Text style={{ color: theme.colors.secondaryText }}>
Manage your personal information.
</Text>
</View>
);
}
const styles = StyleSheet.create({
card: {
padding: 16,
borderWidth: 1,
borderRadius: 12
},
title: {
fontSize: 18,
fontWeight: '700',
marginBottom: 8
}
});
7.2 Using the Component
import React from 'react';
import { View, useColorScheme } from 'react-native';
import { lightTheme, darkTheme } from './src/theme/colors';
import ProfileCard from './src/components/ProfileCard';
export default function App() {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? darkTheme : lightTheme;
return (
<View style={{ flex: 1, backgroundColor: theme.colors.background }}>
<ProfileCard theme={theme} />
</View>
);
}
This approach works well for small projects. In a larger application, React Context can distribute the theme without passing the same prop through many layers of components.
8. Implementing a Manual Theme Switcher
Some applications let users select light or dark mode through an in-app setting. This requires theme state because the user's preference may differ from the system setting.
8.1 Understanding Theme Preferences
- System: Follow the device's current appearance.
- Light: Always use the light theme within the app.
- Dark: Always use the dark theme within the app.
It is useful to store the user's preference as "system", "light", or "dark". The active theme is then calculated from that preference and the system scheme.
8.2 Complete Example with a Theme Toggle
import React, { useState } from 'react';
import {
View,
Text,
Switch,
StyleSheet,
useColorScheme
} from 'react-native';
export default function App() {
const systemScheme = useColorScheme();
const [darkMode, setDarkMode] = useState(
systemScheme === 'dark'
);
const theme = darkMode
? {
background: '#121212',
text: '#FFFFFF',
surface: '#242424',
primary: '#8AB4F8'
}
: {
background: '#FFFFFF',
text: '#222222',
surface: '#F2F2F2',
primary: '#2563EB'
};
return (
<View
style={[
styles.container,
{ backgroundColor: theme.background }
]}
>
<Text style={[styles.title, { color: theme.text }]}>
Theme Settings
</Text>
<View
style={[
styles.row,
{ backgroundColor: theme.surface }
]}
>
<Text style={{ color: theme.text }}>
Enable Dark Mode
</Text>
<Switch
value={darkMode}
onValueChange={setDarkMode}
accessibilityLabel="Enable dark mode"
/>
</View>
<Text style={{ color: theme.primary, marginTop: 20 }}>
{darkMode ? 'Dark mode is active' : 'Light mode is active'}
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
justifyContent: 'center'
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20
},
row: {
padding: 16,
borderRadius: 12,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
}
});
8.3 Important Detail About This Example
The switch controls local state, so it can change the theme manually. However, after the initial state is created, the example does not automatically synchronize the switch with later system changes. A production application should explicitly implement the three preference modes and determine the active theme from the selected preference and current system scheme.
9. Supporting System, Light, and Dark Preferences
A robust implementation distinguishes the user's selected preference from the active theme. For example, if the user selects system, the application follows the device. If the user selects dark, it stays dark even if the device is set to light mode.
9.1 Theme Selection Logic
type ThemePreference = 'system' | 'light' | 'dark';
function getActiveScheme(
preference: ThemePreference,
systemScheme: 'light' | 'dark' | null
): 'light' | 'dark' {
if (preference === 'light') {
return 'light';
}
if (preference === 'dark') {
return 'dark';
}
return systemScheme === 'dark' ? 'dark' : 'light';
}
9.2 Using the Logic in a Component
import React, { useState } from 'react';
import {
View,
Text,
Pressable,
useColorScheme
} from 'react-native';
import { lightTheme, darkTheme } from './src/theme/colors';
type ThemePreference = 'system' | 'light' | 'dark';
export default function App() {
const systemScheme = useColorScheme();
const [preference, setPreference] =
useState<ThemePreference>('system');
const activeScheme =
preference === 'system'
? (systemScheme === 'dark' ? 'dark' : 'light')
: preference;
const theme =
activeScheme === 'dark' ? darkTheme : lightTheme;
return (
<View
style={{
flex: 1,
padding: 24,
backgroundColor: theme.colors.background
}}
>
<Text
style={{
fontSize: 24,
color: theme.colors.text,
marginBottom: 20
}}
>
Choose Appearance
</Text>
{(['system', 'light', 'dark'] as const).map(option => (
<Pressable
key={option}
accessibilityRole="button"
accessibilityState={{
selected: preference === option
}}
onPress={() => setPreference(option)}
style={{
padding: 14,
marginBottom: 10,
backgroundColor: theme.colors.surface,
borderColor: theme.colors.border,
borderWidth: preference === option ? 2 : 1,
borderRadius: 8
}}
>
<Text style={{ color: theme.colors.text }}>
{option === 'system'
? 'Follow System'
: option === 'light'
? 'Light Mode'
: 'Dark Mode'}
</Text>
</Pressable>
))}
</View>
);
}
9.3 How It Works
- The component reads the system scheme through
useColorScheme.
- The
preference state stores the user's selected mode.
- If the preference is light or dark, that choice determines the active theme.
- If the preference is system, the active theme follows the system scheme.
- The chosen theme supplies the colors used by the screen.
10. Sharing Theme State with React Context
As an application grows, many components need access to the active theme. React Context provides a way to share theme state and update functions without manually passing props through every component.
10.1 Suggested Architecture
src/
├── theme/
│ ├── colors.ts
│ ├── ThemeContext.tsx
│ └── useAppTheme.ts
├── components/
│ ├── ThemedText.tsx
│ └── ThemedCard.tsx
└── screens/
├── HomeScreen.tsx
└── SettingsScreen.tsx
10.2 Creating a Theme Context
import React, {
createContext,
useContext,
useMemo,
useState
} from 'react';
import { useColorScheme } from 'react-native';
import { lightTheme, darkTheme } from './colors';
type ThemePreference = 'system' | 'light' | 'dark';
type ThemeContextValue = {
preference: ThemePreference;
setPreference: (value: ThemePreference) => void;
theme: typeof lightTheme | typeof darkTheme;
};
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }) {
const systemScheme = useColorScheme();
const [preference, setPreference] =
useState<ThemePreference>('system');
const activeScheme =
preference === 'system'
? systemScheme === 'dark' ? 'dark' : 'light'
: preference;
const theme =
activeScheme === 'dark' ? darkTheme : lightTheme;
const value = useMemo(
() => ({ preference, setPreference, theme }),
[preference, theme]
);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
export function useAppTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error(
'useAppTheme must be used inside ThemeProvider'
);
}
return context;
}
10.3 Wrapping the Application
import React from 'react';
import { ThemeProvider } from './src/theme/ThemeContext';
import HomeScreen from './src/screens/HomeScreen';
export default function App() {
return (
<ThemeProvider>
<HomeScreen />
</ThemeProvider>
);
}
10.4 Accessing the Theme in a Screen
import React from 'react';
import { View, Text } from 'react-native';
import { useAppTheme } from '../theme/ThemeContext';
export default function HomeScreen() {
const { theme } = useAppTheme();
return (
<View
style={{
flex: 1,
padding: 24,
backgroundColor: theme.colors.background
}}
>
<Text style={{ color: theme.colors.text }}>
Themed Home Screen
</Text>
</View>
);
}
Note: The example demonstrates the main architecture. In a production TypeScript project, define explicit prop types for providers and components, and ensure the theme object types are compatible.
11. Persisting Theme Preferences
When a user manually chooses a theme, they usually expect that choice to remain after closing and reopening the application. This requires storing the selected preference locally.
11.1 What Should Be Stored?
Store the preference rather than only the active color scheme. For example, save system, light, or dark. If the user selects system mode, the application can still respond to later changes in device settings.
11.2 Using AsyncStorage
AsyncStorage is a persistent key-value storage option commonly used in React Native projects. Install it according to the current official documentation and the requirements of your project.
Documentation: React Native AsyncStorage.
11.3 Example: Saving and Loading the Preference
import AsyncStorage from '@react-native-async-storage/async-storage';
const THEME_KEY = '@app_theme_preference';
export async function saveThemePreference(
preference: 'system' | 'light' | 'dark'
) {
try {
await AsyncStorage.setItem(THEME_KEY, preference);
} catch (error) {
console.error('Unable to save theme preference', error);
}
}
export async function loadThemePreference():
Promise<'system' | 'light' | 'dark'> {
try {
const value = await AsyncStorage.getItem(THEME_KEY);
if (value === 'light' || value === 'dark' || value === 'system') {
return value;
}
} catch (error) {
console.error('Unable to load theme preference', error);
}
return 'system';
}
11.4 Integrating Persistence
When the application starts, load the stored preference and use it to initialize theme state. When the user changes the preference, update the state and save the new value. Consider a loading state if the application must avoid briefly displaying the wrong theme before the stored preference is loaded.
For production applications, handle storage errors and avoid overwriting a saved preference with the default value before the initial read has completed.
12. Implementing Themes with React Native Paper
React Native Paper is a Material Design component library that supports light and dark themes. It provides PaperProvider, theme objects, and the useTheme hook.
Official documentation: React Native Paper Theming Guide.
12.1 Installing React Native Paper
npm install react-native-paper
Check the current installation guide for any additional requirements for your React Native or Expo project.
12.2 Using Built-in Light and Dark Themes
import React from 'react';
import {
PaperProvider,
MD3LightTheme,
MD3DarkTheme,
Button,
Card,
Text
} from 'react-native-paper';
import { useColorScheme, View } from 'react-native';
export default function App() {
const colorScheme = useColorScheme();
const theme =
colorScheme === 'dark'
? MD3DarkTheme
: MD3LightTheme;
return (
<PaperProvider theme={theme}>
<View style={{ padding: 20 }}>
<Text variant="headlineMedium">
Theme Example
</Text>
<Card style={{ marginVertical: 16 }}>
<Card.Content>
<Text variant="titleLarge">
React Native Paper
</Text>
<Text>
This interface follows the device appearance.
</Text>
</Card.Content>
</Card>
<Button mode="contained" onPress={() => {}}>
Continue
</Button>
</View>
</PaperProvider>
);
}
12.3 Explanation
MD3LightTheme provides the Material Design 3 light theme.
MD3DarkTheme provides the corresponding dark theme.
useColorScheme detects the current system preference.
PaperProvider makes the selected theme available to Paper components.
- Paper components use theme values for their appearance.
12.4 Customizing the Theme
import {
MD3LightTheme,
MD3DarkTheme
} from 'react-native-paper';
export const customLightTheme = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
primary: '#1D4ED8',
secondary: '#0F766E',
background: '#FFFFFF'
}
};
export const customDarkTheme = {
...MD3DarkTheme,
colors: {
...MD3DarkTheme.colors,
primary: '#93C5FD',
secondary: '#5EEAD4',
background: '#121212'
}
};
Extend the appropriate default theme and preserve the other required theme properties. Theme structures may vary by library version, so consult the current documentation before adding or overriding values.
13. Theming Common UI Elements
Changing only the screen background is not sufficient. Every visible interface element must be reviewed to ensure the selected theme is applied consistently.
13.1 Buttons
- Choose a primary button color that contrasts with its label.
- Check pressed, focused, and disabled states.
- Ensure destructive buttons remain distinguishable in both themes.
13.2 Cards
- Use a suitable surface color for cards.
- Maintain visible separation from the screen background.
- Adjust borders and shadows for dark surfaces.
13.3 Text Inputs
- Update the input background and text color.
- Use visible placeholder and label colors.
- Keep error messages readable.
- Ensure focus borders are visible in both themes.
13.4 Navigation Bars and Status Bars
Navigation elements and system bars may require separate configuration depending on the navigation library and platform. Check status bar icon color, navigation bar background, safe areas, and modal appearance on both Android and iOS.
13.5 Images and Icons
Some image assets are suitable for both themes, while others need theme-specific versions. Logos, illustrations, and icons should be checked for visibility against light and dark backgrounds.
14. Designing Accessible Light and Dark Themes
Accessibility is an essential part of theme implementation. A dark theme is not automatically accessible simply because it uses dark colors. Text, icons, borders, and interactive states must remain clear in both modes.
14.1 Contrast
Maintain sufficient contrast between text and its background. For ordinary text, WCAG commonly recommends a contrast ratio of at least 4.5:1, while large text generally requires at least 3:1. These are useful design checks, although platform-specific requirements and context should also be considered.
14.2 Accessibility Best Practices
- Do not use color as the only indicator of errors or success.
- Use accessible labels for icon-only controls.
- Keep text readable when users increase font size.
- Check focus indicators and disabled states.
- Test light and dark modes using screen readers.
- Avoid pure white text on pure black backgrounds if the resulting appearance is uncomfortable for your intended audience.
- Check color contrast for links, placeholders, dividers, and secondary text.
15. Testing Light and Dark Themes
Theme testing verifies that the entire interface updates correctly when the user changes the system appearance or in-app preference.
15.1 Testing Checklist
- Open the application with the device set to light mode.
- Switch the device to dark mode while the application is running.
- Confirm that screen backgrounds and text update.
- Check cards, dialogs, menus, and input fields.
- Test the manual theme preference if the application provides one.
- Confirm that system mode follows later device setting changes.
- Close and reopen the application to test saved preferences.
- Check navigation bars and status bar icons.
- Inspect images and icons for visibility.
- Test both Android and iOS where applicable.
15.2 Testing in Expo
For an Expo project, use the supported appearance configuration for the installed SDK. Expo documents the userInterfaceStyle setting and automatic system appearance behavior.
Documentation: Expo Color Themes.
16. Common Problems and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Theme does not change with device settings |
Color scheme is read once or the hook is not used correctly |
Use useColorScheme in the component that needs the current scheme. |
| Only the background changes |
Text, cards, and controls use fixed colors |
Use centralized theme colors throughout the interface. |
| Manual switch does not follow system changes |
Manual state is being used without a system preference mode |
Separate the stored preference from the calculated active scheme. |
| Saved preference is lost |
Preference exists only in component state |
Persist the user's selection using suitable local storage. |
| Wrong theme appears briefly at startup |
Stored preference is loaded asynchronously |
Wait for the initial preference load or provide an appropriate startup state. |
| Text is difficult to read |
Insufficient contrast or unsuitable secondary text colors |
Adjust the palette and test contrast. |
| React Native Paper components use unexpected colors |
Provider or theme configuration is incorrect |
Verify the selected theme and the PaperProvider setup. |
| Icons disappear in dark mode |
Fixed dark icon assets are used on dark backgrounds |
Use theme-aware icon colors or suitable alternate assets. |
| Status bar remains difficult to read |
System bar style is not coordinated with the theme |
Configure and test status bar appearance for each platform. |
| Theme differs between platforms |
Native controls or platform defaults behave differently |
Test on real target platforms and adjust supported platform-specific settings. |
17. Best Practices for Theme Implementation
- Centralize colors: Define theme palettes in a dedicated module.
- Use semantic names: Prefer
background, surface, text, and primary over names tied to a specific color.
- Separate preference from appearance: Keep the user's system/light/dark choice separate from the currently active scheme.
- Reuse theme values: Avoid hardcoding colors throughout the application.
- Support system changes: Use
useColorScheme when system preference should be observed.
- Persist explicit choices: Store manual preferences if they should survive application restarts.
- Test every component: Review dialogs, menus, loading states, forms, and error messages.
- Check accessibility: Validate contrast, text scaling, and screen-reader support.
- Minimize flicker: Handle asynchronous preference loading carefully.
- Document theme tokens: Help the team understand which token to use for each UI purpose.
18. Mini Project: Light and Dark Theme Settings Screen
Project objective: Build a settings screen that lets users choose system, light, or dark mode and applies the selected theme to the entire application.
18.1 Requirements
- A settings screen with three theme options.
- System mode that follows the device appearance.
- A light theme with appropriate background and text colors.
- A dark theme with readable text and visible controls.
- Reusable theme configuration.
- Persistent storage for the user's preference.
- Consistent theme application across multiple screens.
- Accessible labels and selected-state indicators.
18.2 Implementation Steps
- Create a React Native project.
- Define the light and dark theme objects.
- Use
useColorScheme to observe the system preference.
- Create a theme context that stores the selected preference.
- Add system, light, and dark options to the settings screen.
- Calculate the active theme from the preference and system scheme.
- Apply theme values to the background, text, cards, and buttons.
- Save the selected preference in local storage.
- Load the saved preference when the application starts.
- Test system changes, app restarts, and accessibility in both modes.
18.3 Expected Result
The application should display the appropriate theme throughout the interface. Selecting light or dark should override the system preference inside the app, while selecting system should restore automatic behavior.
19. Practice Exercises
- Create a basic screen that changes its background and text colors according to the device theme.
- Create light and dark theme objects in a separate file.
- Build a reusable themed card component.
- Implement a switch for manually changing the theme.
- Extend the switch implementation to support system, light, and dark preferences.
- Use React Context to share theme state across multiple screens.
- Save and restore the user's selected theme with AsyncStorage.
- Integrate theme switching with React Native Paper.
- Check contrast ratios for primary and secondary text in both themes.
- Test theme behavior on Android and iOS devices or simulators.
20. Interview Questions and Answers
Q1. What is a theme in React Native?
Answer: A theme is a collection of visual settings, such as colors and other design values, that defines how an application's interface appears.
Q2. What is the difference between light mode and dark mode?
Answer: Light mode generally uses light backgrounds and dark text, whereas dark mode uses dark backgrounds and lighter text.
Q3. What is the useColorScheme hook?
Answer: It is a React Native hook that provides the current preferred color scheme and subscribes to appearance changes.
Q4. What values can useColorScheme return?
Answer: It commonly returns light, dark, or null if the preference is unavailable.
Q5. What is the Appearance API?
Answer: It provides methods for reading the preferred appearance, listening for changes, and controlling the application's appearance override where supported.
Q6. Why should theme colors be centralized?
Answer: Centralized theme values reduce duplication, improve consistency, and make it easier to update the visual design.
Q7. How can a user manually change themes?
Answer: Store a theme preference in state and provide controls that select light or dark mode. Calculate the active theme from that preference.
Q8. How can the application follow the system theme?
Answer: Use useColorScheme and select the light or dark theme based on the returned value. An explicit system preference should use this behavior instead of forcing one mode.
Q9. How can a theme preference persist after restarting the application?
Answer: Store the preference in persistent local storage, load it when the application starts, and update it whenever the user changes the setting.
Q10. What is React Native Paper?
Answer: React Native Paper is a Material Design component library that supports themes through PaperProvider and theme objects.
Q11. Why is accessibility important for dark themes?
Answer: Text and controls must remain readable and distinguishable. Developers should verify contrast, text scaling, focus states, and screen-reader behavior.
Q12. What causes theme flickering on startup?
Answer: The application may render a default theme before an asynchronously stored preference has loaded. A suitable initial loading strategy can reduce this issue.
21. Quick Revision Checklist
- Understand light mode, dark mode, and system mode.
- Use
useColorScheme to observe system appearance changes.
- Understand the purpose of the
Appearance API.
- Create centralized light and dark theme objects.
- Apply semantic theme values to every visible component.
- Separate the selected preference from the active theme.
- Use React Context when multiple screens need shared theme state.
- Persist user-selected preferences when required.
- Configure theme providers when using UI libraries such as React Native Paper.
- Test contrast, icons, system bars, forms, dialogs, and navigation.
- Verify behavior on the supported Android and iOS versions.
22. Official Learning Resources
23. Conclusion
Implementing light and dark themes in React Native makes an application more adaptable and gives users greater control over its appearance. The useColorScheme hook supports system-based theme detection, while centralized theme objects make styling consistent and maintainable.
For larger applications, React Context can distribute the active theme, and local storage can preserve explicit user preferences. UI libraries such as React Native Paper provide additional theme support for their components. A complete implementation should update the entire interface, respect system changes when requested, preserve user choices, and maintain accessibility in both modes.
To continue learning, explore the React Native Training course at JustAcademy or register for a course demo.