Fix: React Native Paper Not Working — Theme Not Applying, Icons Missing, or Components Unstyled
Quick Answer
How to fix React Native Paper issues — PaperProvider setup, Material Design 3 theming, custom color schemes, icon configuration, dark mode, and Expo integration.
The Problem
Paper components render but look plain and unstyled:
import { Button, Text } from 'react-native-paper';
function App() {
return (
<Button mode="contained">Press me</Button>
);
}
// Button renders as plain text — no Material Design stylingOr icons don’t show up:
<Button icon="camera">Take Photo</Button>
// Button renders without the camera iconOr the custom theme doesn’t apply:
const theme = { colors: { primary: '#6200ee' } };
<PaperProvider theme={theme}>
<Button mode="contained">Themed Button</Button>
</PaperProvider>
// Button still uses default colorsWhy This Happens
React Native Paper implements Material Design for React Native. Common issues:
PaperProvidermust wrap the entire app — all Paper components read theme values from the provider context. Without it, components fall back to unstyled defaults or crash.- Vector icons must be loaded — Paper uses
@expo/vector-icons(MaterialCommunityIcons). In bare React Native (non-Expo),react-native-vector-iconsmust be installed and linked separately. - Paper v5 uses Material Design 3 — the theming API changed from v4 to v5.
MD3LightThemeandMD3DarkThemereplace the v4DefaultThemeandDarkTheme. Mixing versions causes styling issues. - Custom themes must extend the base theme — passing a partial theme object overrides the entire theme, losing default values. Use
MD3LightThemeas the base and spread your customizations.
Fix 1: Setup with Expo
npx expo install react-native-paper react-native-safe-area-context// App.tsx or app/_layout.tsx
import { PaperProvider, MD3LightTheme, MD3DarkTheme } from 'react-native-paper';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { useColorScheme } from 'react-native';
// Custom theme extending Material Design 3
const lightTheme = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
primary: '#6750A4',
secondary: '#625B71',
tertiary: '#7D5260',
// Custom colors
brand: '#3b82f6',
},
roundness: 12, // Border radius for components
};
const darkTheme = {
...MD3DarkTheme,
colors: {
...MD3DarkTheme.colors,
primary: '#D0BCFF',
secondary: '#CCC2DC',
tertiary: '#EFB8C8',
brand: '#60a5fa',
},
roundness: 12,
};
export default function App() {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? darkTheme : lightTheme;
return (
<SafeAreaProvider>
<PaperProvider theme={theme}>
<AppContent />
</PaperProvider>
</SafeAreaProvider>
);
}
// Type the custom theme
type AppTheme = typeof lightTheme;
declare global {
namespace ReactNativePaper {
interface Theme extends AppTheme {}
}
}// babel.config.js — optional: reduce bundle size
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
env: {
production: {
plugins: ['react-native-paper/babel'], // Tree-shaking unused components
},
},
};
};Fix 2: Core Components
import {
Appbar, Button, Card, Text, TextInput, FAB, Chip, Avatar,
Badge, Banner, Divider, List, Menu, Searchbar, SegmentedButtons,
Snackbar, Surface, Switch, ToggleButton, IconButton,
ProgressBar, ActivityIndicator, Dialog, Portal,
} from 'react-native-paper';
import { View, ScrollView } from 'react-native';
import { useState } from 'react';
function HomeScreen({ navigation }) {
const [searchQuery, setSearchQuery] = useState('');
return (
<View style={{ flex: 1 }}>
{/* App bar */}
<Appbar.Header>
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Home" />
<Appbar.Action icon="magnify" onPress={() => {}} />
<Appbar.Action icon="dots-vertical" onPress={() => {}} />
</Appbar.Header>
<ScrollView style={{ flex: 1, padding: 16 }}>
{/* Search */}
<Searchbar
placeholder="Search"
value={searchQuery}
onChangeText={setSearchQuery}
style={{ marginBottom: 16 }}
/>
{/* Card */}
<Card style={{ marginBottom: 16 }}>
<Card.Cover source={{ uri: 'https://picsum.photos/700' }} />
<Card.Title
title="Card Title"
subtitle="Card Subtitle"
left={(props) => <Avatar.Icon {...props} icon="account" />}
/>
<Card.Content>
<Text variant="bodyMedium">
This is a Material Design 3 card with cover image.
</Text>
</Card.Content>
<Card.Actions>
<Button>Cancel</Button>
<Button mode="contained">OK</Button>
</Card.Actions>
</Card>
{/* Button variants */}
<View style={{ gap: 8, marginBottom: 16 }}>
<Button mode="contained" onPress={() => {}}>Contained</Button>
<Button mode="outlined" onPress={() => {}}>Outlined</Button>
<Button mode="text" onPress={() => {}}>Text</Button>
<Button mode="elevated" onPress={() => {}}>Elevated</Button>
<Button mode="contained-tonal" onPress={() => {}}>Tonal</Button>
<Button mode="contained" icon="camera" onPress={() => {}}>With Icon</Button>
<Button mode="contained" loading onPress={() => {}}>Loading</Button>
</View>
{/* Chips */}
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginBottom: 16 }}>
<Chip icon="tag" onPress={() => {}}>Tag</Chip>
<Chip selected onPress={() => {}}>Selected</Chip>
<Chip icon="close" onClose={() => {}}>Removable</Chip>
</View>
{/* Text input */}
<TextInput
label="Email"
mode="outlined"
placeholder="[email protected]"
left={<TextInput.Icon icon="email" />}
style={{ marginBottom: 16 }}
/>
<TextInput
label="Password"
mode="outlined"
secureTextEntry
right={<TextInput.Icon icon="eye" />}
style={{ marginBottom: 16 }}
/>
</ScrollView>
{/* FAB */}
<FAB
icon="plus"
style={{ position: 'absolute', right: 16, bottom: 16 }}
onPress={() => {}}
/>
</View>
);
}Fix 3: Dialogs and Modals
import { Portal, Dialog, Button, Text, TextInput } from 'react-native-paper';
import { useState } from 'react';
function DialogExample() {
const [visible, setVisible] = useState(false);
const [input, setInput] = useState('');
return (
<>
<Button onPress={() => setVisible(true)}>Show Dialog</Button>
<Portal>
<Dialog visible={visible} onDismiss={() => setVisible(false)}>
<Dialog.Title>Create Item</Dialog.Title>
<Dialog.Content>
<Text variant="bodyMedium" style={{ marginBottom: 12 }}>
Enter a name for your new item.
</Text>
<TextInput
label="Name"
mode="outlined"
value={input}
onChangeText={setInput}
/>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={() => setVisible(false)}>Cancel</Button>
<Button onPress={() => { handleCreate(input); setVisible(false); }}>
Create
</Button>
</Dialog.Actions>
</Dialog>
</Portal>
</>
);
}
// Snackbar (toast-like notification)
function SnackbarExample() {
const [visible, setVisible] = useState(false);
return (
<>
<Button onPress={() => setVisible(true)}>Show Snackbar</Button>
<Snackbar
visible={visible}
onDismiss={() => setVisible(false)}
duration={3000}
action={{
label: 'Undo',
onPress: () => { /* undo action */ },
}}
>
Item deleted successfully
</Snackbar>
</>
);
}Fix 4: List Components
import { List, Divider, Switch } from 'react-native-paper';
import { ScrollView } from 'react-native';
import { useState } from 'react';
function SettingsList() {
const [notifications, setNotifications] = useState(true);
const [darkMode, setDarkMode] = useState(false);
return (
<ScrollView>
<List.Section>
<List.Subheader>Account</List.Subheader>
<List.Item
title="Profile"
description="Edit your profile information"
left={(props) => <List.Icon {...props} icon="account" />}
right={(props) => <List.Icon {...props} icon="chevron-right" />}
onPress={() => {}}
/>
<Divider />
<List.Item
title="Security"
description="Password and two-factor authentication"
left={(props) => <List.Icon {...props} icon="shield-lock" />}
right={(props) => <List.Icon {...props} icon="chevron-right" />}
onPress={() => {}}
/>
</List.Section>
<List.Section>
<List.Subheader>Preferences</List.Subheader>
<List.Item
title="Notifications"
description="Push and email notifications"
left={(props) => <List.Icon {...props} icon="bell" />}
right={() => (
<Switch value={notifications} onValueChange={setNotifications} />
)}
/>
<Divider />
<List.Item
title="Dark Mode"
left={(props) => <List.Icon {...props} icon="brightness-6" />}
right={() => (
<Switch value={darkMode} onValueChange={setDarkMode} />
)}
/>
</List.Section>
{/* Expandable list */}
<List.AccordionGroup>
<List.Accordion title="Advanced" id="1" left={(props) => <List.Icon {...props} icon="cog" />}>
<List.Item title="Cache" description="Clear app cache" onPress={() => {}} />
<List.Item title="Data" description="Export your data" onPress={() => {}} />
<List.Item title="Logs" description="View debug logs" onPress={() => {}} />
</List.Accordion>
</List.AccordionGroup>
</ScrollView>
);
}Fix 5: Dynamic Theme with useTheme
import { useTheme, MD3Theme } from 'react-native-paper';
import { View, StyleSheet } from 'react-native';
function ThemedComponent() {
const theme = useTheme<MD3Theme>();
return (
<View style={[styles.container, { backgroundColor: theme.colors.surface }]}>
<View style={[styles.card, {
backgroundColor: theme.colors.surfaceVariant,
borderRadius: theme.roundness,
}]}>
<Text style={{ color: theme.colors.onSurfaceVariant }}>
Themed card using hook
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
card: { padding: 16 },
});Fix 6: Custom Colors with Material You
// Generate a full Material Design 3 color scheme from a seed color
import { MD3LightTheme, configureFonts } from 'react-native-paper';
// Use Material Theme Builder: https://m3.material.io/theme-builder
// Export your color scheme and apply it:
const customColors = {
primary: '#6750A4',
onPrimary: '#FFFFFF',
primaryContainer: '#EADDFF',
onPrimaryContainer: '#21005D',
secondary: '#625B71',
onSecondary: '#FFFFFF',
secondaryContainer: '#E8DEF8',
onSecondaryContainer: '#1D192B',
// ... full MD3 color palette
surface: '#FFFBFE',
onSurface: '#1C1B1F',
surfaceVariant: '#E7E0EC',
onSurfaceVariant: '#49454F',
error: '#B3261E',
onError: '#FFFFFF',
};
const theme = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
...customColors,
},
};Still Not Working?
Components are unstyled — PaperProvider must wrap your entire app. Every Paper component reads theme values from context. Without the provider, components render with browser/system defaults.
Icons are missing (empty space where icon should be) — Paper uses MaterialCommunityIcons. In Expo, @expo/vector-icons is pre-installed. In bare React Native, install react-native-vector-icons and link it. Check the icon name at materialdesignicons.com.
Custom theme colors don’t apply — don’t pass a partial theme. Always spread the base theme: { ...MD3LightTheme, colors: { ...MD3LightTheme.colors, primary: '#xxx' } }. A partial object replaces all colors, leaving most undefined.
v4 code doesn’t work in v5 — DefaultTheme and DarkTheme were replaced with MD3LightTheme and MD3DarkTheme. The colors shape changed completely for Material Design 3. Check the migration guide for renamed properties.
For related mobile UI issues, see Fix: Tamagui Not Working and Fix: NativeWind Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Expo Router Not Working — Routes Not Matching, Layout Nesting Wrong, or Deep Links Failing
How to fix Expo Router issues — file-based routing, layout routes, dynamic segments, tabs and stack navigation, modal routes, authentication flows, and deep linking configuration.
Fix: React Navigation Not Working — Screens Not Rendering, TypeScript Errors, or Gestures Broken
How to fix React Navigation issues — stack and tab navigator setup, TypeScript typing, deep linking, screen options, nested navigators, authentication flow, and performance optimization.
Fix: NativeWind Not Working — Styles Not Applying, Dark Mode Broken, or Metro Bundler Errors
How to fix NativeWind issues — Tailwind CSS for React Native setup, Metro bundler configuration, className prop, dark mode, responsive styles, and Expo integration.
Fix: Tamagui Not Working — Styles Not Applying, Compiler Errors, or Web/Native Mismatch
How to fix Tamagui UI kit issues — setup with Expo, theme tokens, styled components, animations, responsive props, media queries, and cross-platform rendering.