Fix: React Native Paper Not Working — Theme Not Applying, Icons Missing, or Components Unstyled
Part of: JavaScript & TypeScript Errors
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.
The deeper reason most “unstyled” problems happen is that Paper components do not have any default visual style of their own at the component level — every color, elevation, and font scale is read from theme context on render. If context is missing, theme is undefined, and the StyleSheet derived from theme falls back to either transparent or empty strings, which React Native silently ignores. The component still mounts and lays out, so there’s no error, just a flat, unstyled element. This is by design (theming is mandatory in Material 3), but it means a missing provider produces no warning in development.
Icon failures have a similar root cause. Paper renders icons by looking up a glyph name in a font file. If the font isn’t bundled into the native binary, the lookup returns an empty box that takes up the icon’s width but draws nothing. With Expo SDK 49+ the font is pre-registered automatically, but a bare React Native project needs an explicit font link via npx react-native-asset or react-native.config.js, plus a rebuild. Hot reload does not pick up new native assets; you must rebuild the iOS/Android binary.
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
},
},
};
};How Other Tools Handle This
Material Design is one philosophy among several in the React Native UI ecosystem. The choice affects bundle size, theming model, and how closely components track each platform’s native look.
NativeBase (now in maintenance) used a utility-prop API on top of styled-system, modeled after Chakra. Theming was JSON-based and any property could be overridden inline, which made customization fast but caused performance issues on large lists because each style was recomputed on every render. Paper recomputes theme-derived styles only when the theme object identity changes, which is why Paper works well with FlatList virtualization while NativeBase did not.
Tamagui takes the opposite approach: themes are compiled at build time via a Babel plugin, producing flat StyleSheets with zero runtime cost. Paper themes are pure runtime — they live in React context and re-render every consumer when the theme changes. Tamagui is faster on cold start and on theme-heavy screens, but Paper’s runtime model lets you swap themes (light/dark, brand variants) without a rebuild and without coordinating a compiler. See Fix: Tamagui Not Working for compiler-side issues that don’t apply to Paper.
UI Kitten is Eva Design rather than Material. It splits “mapping” (component-to-style rules) from theme (color tokens), which is more flexible but has a steeper learning curve. Paper’s MD3 theme is opinionated — you can change colors and roundness but not the underlying design language. If your app needs strict Material Design 3 fidelity for a Google Play submission, Paper is the only mainstream choice.
gluestack-ui (the NativeBase successor) embraces utility classes via NativeWind and aims for cross-platform parity with Tailwind. It’s the closest React Native equivalent to shadcn/ui — primitives you copy into your codebase. Paper ships as a black-box library; gluestack ships as source. If you want to fork a Button to behave differently, gluestack is friendlier. See Fix: NativeWind Not Working for the styling pipeline that backs it.
Material vs platform-native is the strategic question. Paper draws Material on iOS too, which can look out of place. If you need each platform to feel native, use platform-conditional imports (Platform.select) and pair Paper with @react-native-community/segmented-control or the new react-native core components for iOS-specific UI. Or pick a library like React Native Elements that aims for platform-aware defaults.
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.
Dialogs and Snackbars don’t appear on screen — they require a <Portal.Host> ancestor. PaperProvider includes one by default, but if you nest providers (for example a per-screen modal stack) the Portal can be mounted into the wrong tree. Wrap the offending screen with an explicit <Portal.Host> or check that only one PaperProvider lives at the root.
Metro fails to resolve react-native-vector-icons — in bare projects, you also need the font copied into the iOS/Android bundle. Run npx react-native-asset after configuring react-native.config.js, then do a clean rebuild (npx react-native run-ios --reset-cache or cd android && ./gradlew clean). See Fix: React Native Metro Bundler Failed for cache-related symptoms that look similar.
Theme switches flicker on app launch — useColorScheme() returns null for one frame before the system value resolves. Wrap the provider with Appearance.getColorScheme() as a synchronous fallback, or set an initial theme in a top-level state initializer so the first render is already correct.
For related mobile UI issues, see Fix: Expo Not Working and Fix: Tamagui 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.