What Paper is, and the decision it makes for you
react-native-paper v5 is a complete implementation of Material Design 3: Button, TextInput, Card, Appbar, FAB, Chip, Dialog, Snackbar, BottomNavigation, Menu, Searchbar, and about thirty more, all themed from one object and all accessible out of the box. It is maintained by Callstack, supports the New Architecture, and runs in Expo Go.
The decision it makes for you is the design language. Material on Android is the platform look. Material on iOS is a visible choice: Material buttons, Material text fields with their floating labels, Material ripples instead of iOS highlight states. Plenty of successful apps make that choice, Google’s own iOS apps among them. Just make it on purpose. If “feels native on iPhone” is on your requirements list, stop here and read the comparison section.
Setup in Expo
npx expo install react-native-paper react-native-safe-area-contextIcons need nothing extra in Expo. Paper renders icons through MaterialCommunityIcons, and Expo ships that font inside @expo/vector-icons. In a bare React Native project you install react-native-vector-icons and link the font yourself; the missing-icon squares people see on first run are always this step.
Then one provider at the root. With Expo Router that is app/_layout.tsx:
// app/_layout.tsx
import { Stack } from 'expo-router';
import { useColorScheme } from 'react-native';
import { PaperProvider, MD3LightTheme, MD3DarkTheme } from 'react-native-paper';
const light = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
primary: '#0f766e',
onPrimary: '#ffffff',
primaryContainer: '#a7f3d0',
onPrimaryContainer: '#064e3b',
secondary: '#f97316',
},
};
const dark = {
...MD3DarkTheme,
colors: {
...MD3DarkTheme.colors,
primary: '#5eead4',
onPrimary: '#042f2e',
primaryContainer: '#115e59',
onPrimaryContainer: '#ccfbf1',
secondary: '#fdba74',
},
};
export default function RootLayout() {
const scheme = useColorScheme();
return (
<PaperProvider theme={scheme === 'dark' ? dark : light}>
<Stack screenOptions={{ headerShown: false }} />
</PaperProvider>
);
}A note on the colour roles. Material 3 defines about thirty of them (primary, onPrimary, primaryContainer, surfaceVariant, outline, and so on) and components read several at once. Overriding only primarygives you teal buttons with a purple ripple and purple chips, because those read other roles that still hold the default. For a coherent palette, generate the full set from a seed colour with Google’s Material Theme Builder, export as JSON, and paste it in. It takes five minutes and saves a week of “why is that purple.”
Using it on a screen
import { useState } from 'react';
import { View } from 'react-native';
import { Appbar, Button, Card, Snackbar, Text, TextInput, useTheme } from 'react-native-paper';
export default function NewHabit() {
const theme = useTheme();
const [name, setName] = useState('');
const [saved, setSaved] = useState(false);
return (
<View style={{ flex: 1, backgroundColor: theme.colors.background }}>
<Appbar.Header>
<Appbar.BackAction onPress={() => {}} />
<Appbar.Content title="New habit" />
</Appbar.Header>
<Card style={{ margin: 16 }} mode="elevated">
<Card.Content style={{ gap: 12 }}>
<TextInput label="Habit name" value={name} onChangeText={setName} mode="outlined" />
<Text variant="bodySmall" style={{ color: theme.colors.onSurfaceVariant }}>
Short names are easier to keep.
</Text>
</Card.Content>
<Card.Actions>
<Button mode="contained" onPress={() => setSaved(true)} disabled={!name.trim()}>
Save
</Button>
</Card.Actions>
</Card>
<Snackbar visible={saved} onDismiss={() => setSaved(false)} duration={2500}>
Habit saved
</Snackbar>
</View>
);
}Everything here is themed and accessible without further work: the TextInput has a floating label and an error state, the Button has a ripple on Android and an opacity press on iOS, and Text variant maps to the MD3 type scale so you never pick a font size by hand. That is the trade Paper offers: you give up the look, you get back every afternoon you would have spent on states.
Three things that trip people up
Portal needs the provider. Dialog, Menu, and Modal in Paper render through a Portal so they float above everything. The Portal host lives inside PaperProvider, so a Dialog rendered outside the provider, or before it mounts, silently shows nothing. Keep PaperProvider at the very root.
The babel plugin is for bundle size, and production only. Paper re-exports every component from one index, so a naive import pulls all of them in. Add react-native-paper/babel to the env.production.plugins array in babel.config.js and the imports are rewritten to individual files in release builds. Do not add it to the default plugins; it slows the dev bundler for no benefit.
Match the navigation theme.If you use React Navigation or Expo Router, its header and background colours come from its own theme, not Paper’s. Paper exports adaptNavigationTheme, which maps your MD3 colours onto the navigation theme object so the two agree. Skipping it is why the Appbar is teal and the screen behind it is a slightly different white.
Paper vs NativeWind vs Tamagui vs gluestack
| Library | What it is | Look | Theming | Pick it when |
|---|---|---|---|---|
React Native Paper | Component library | Material 3, on both platforms | MD3 theme object, ~30 colour roles | Ship a finished-looking app fast; Android-first or internal tools |
NativeWind | Styling (Tailwind) | Whatever you build | tailwind.config + CSS variables | Custom design, team already knows Tailwind |
Tamagui | Styling + components | Neutral, highly customisable | Tokens, compiled at build time | Universal web + native apps, performance-critical UI |
gluestack-ui | Copy-paste components | Neutral, shadcn-style | Tailwind via NativeWind | You want to own the component code from day one |
React Native Elements | Component library | Neutral, older | ThemeProvider | Legacy projects already on it |
The useful split is component library versus styling system. Paper gives you finished components and a fixed look. NativeWind gives you a way to style and no components; gluestack sits on top of it with copy-paste components you then own; Tamagui is its own styling compiler with an optional component kit. None of the three has an opinion about Material, which is exactly why teams that care about an iOS-native feel end up on them.
Mixing is fine and common: Paper for Dialog, Snackbar, and TextInput where the Material behaviour is genuinely good, plain Views with your own styling for the screens that carry the brand. Just wrap the Paper pieces in a theme that matches your palette so they do not look imported.
Our default, and why
Apps generated with ShipNative use NativeWind and plain React Native primitives rather than Paper, because each app gets its own design from the prompt and a Material skin would flatten them into the same look. The trade-off is real: the generator has to produce every pressed state and error state itself, which Paper would hand you for free. If you are building by hand and shipping an Android-first or internal app, Paper is the faster road, and the setup above is all of it.