react-native-paper Documentation

Material Design for React Native (Android & iOS)

14,472 stars TypeScript TypeScript 12 files Β· ~14,489 tokens #hacktoberfest#material-design#react-native#ui-kit
RAW Doc

Technical Documentation: callstack/react-native-paper

ℹ️ Provenance: Multi-Tier Ingestion Β· CodeWiki Reference

Overview (`README.md`)


Build Status
Version
MIT License
All Contributors
PRs Welcome
Chat
Sponsored by Callstack

Features

Currently supported React Native version: >= 0.50.3

Try it out

Run the example app with Expo to see it in action.

The source code for the examples are under the /example folder.

Getting Started

Refer to the getting started guide for instructions.

Documentation

Check the components and their usage in our documentation.

Contributing

Read the contribution guidelines before contributing.

Figma and Sketch component kits

Use official component kits provided by Material Design.

Made with ❀️ at Callstack

react-native-paper is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. Callstack is a group of React and React Native geeks, contact us at [email protected] if you need any help with these or just want to say hi!

Contributors

Thanks goes to these wonderful people (emoji key):

Satyajit SahooπŸ€” πŸ’» πŸ“– Ferran NegreπŸ€” πŸ’» DawidπŸ€” πŸ’» πŸ“– Kacper WiszczukπŸ€” πŸ’» Luke WalczakπŸ’» πŸ“– Ahmed ElhanafyπŸ€” πŸ’» K. P. SrokaπŸ’» πŸ“–
Iyad ThayyilπŸ’» πŸ“– Julian HundelohπŸ’» πŸ“– Grzegorz GawrysiakπŸ’» πŸ“– LuΓ­sπŸ’» Rajendran NadarπŸ’» Brent VatneπŸ’» Jakub BeneΕ‘πŸ’»
PaweΕ‚ SzymaΕ„skiπŸ’» πŸ“– KubaπŸ’» πŸ€” jbindaπŸ’» πŸ€”

This project follows the all-contributors specification. Contributions of any kind welcome!


Overview (`example/README.md`)

Example App for React Native Paper

React Native App

You can run the React Native app with this Snack. Snack allows you to make changes to the example app directly in the online editor and see changes on your phone instantly using the Expo app without having to install or setup anything on your computer. You can also "Export" it to download as a standalone Expo app to run locally.

If you want to run the example from the repo,

  • Clone the repository and run yarn in the root
  • Run yarn example start to start the packager
  • Follow the instructions to open it with the Expo app

Web App

You can also run the example app as a web app using react-native-web,

  • Clone the repository and run yarn in the root
  • Run yarn example web to start the webpack server and open the app in your browser

Pages 1.Getting Started (`docs/pages/1.getting-started.md`)


title: Getting Started

Getting Started

Installation

Open a Terminal in your project's folder and run:

sh
yarn add react-native-paper

or

sh
npm install react-native-paper

If you're on a vanilla React Native project, you also need to install and link react-native-vector-icons.

Specifically MaterialCommunityIcons icon pack needs to be included in the project, because some components use those internally (e.g. AppBar.BackAction on Android).

sh
yarn add react-native-vector-icons
react-native link react-native-vector-icons

If you don't want to install vector icons, you can use babel-plugin-optional-require to opt-out.

If you use Expo, you don't need to install vector icons. But if you have a babel.config.js or .babelrc file, make sure that it includes babel-preset-expo.

To get smaller bundle size by excluding modules you don't use, you can use our optional babel plugin. The plugin automatically rewrites the import statements so that only the modules you use are imported instead of the whole library. Add react-native-paper/babel to the plugins section in your babel.config.js for production environment. It should look like this:

js
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  env: {
    production: {
      plugins: ['react-native-paper/babel'],
    },
  },
};

If you created your project using Expo, it'll look something like this:

js
module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    env: {
      production: {
        plugins: ['react-native-paper/babel'],
      },
    },
  };
};

The plugin only works if you are importing the library using ES2015 import statements and not with require.

Note: The above examples are for the latest react-native using Babel 7. If you have react-native <= 0.55, you'll have a .babelrc file instead of a babel.config.js file and the content of the file will be different.

If you're using Flow for typechecking your code, you need to add the following under the [options] section in your .flowconfig:

ini
module.file_ext=.js
module.file_ext=.native.js
module.file_ext=.android.js
module.file_ext=.ios.js

Usage

Wrap your root component in Provider from react-native-paper. If you have a vanilla React Native project, it's a good idea to add it in the component which is passed to AppRegistry.registerComponent. This will usually be in the index.js file. If you have an Expo project, you can do this inside the exported component in the App.js file.

Example:

js
import * as React from 'react';
import { AppRegistry } from 'react-native';
import { Provider as PaperProvider } from 'react-native-paper';
import { name as appName } from './app.json';
import App from './src/App';

export default function Main() {
  return (
    <PaperProvider>
      <App />
    </PaperProvider>
  );
}

AppRegistry.registerComponent(appName, () => Main);

The PaperProvider component provides the theme to all the components in the framework. It also acts as a portal to components which need to be rendered at the top level.

If you have another provider (such as Redux), wrap it outside PaperProvider so that the context is available to components rendered inside a Modal from the library:

js
import * as React from 'react';
import { Provider as PaperProvider } from 'react-native-paper';
import { Provider as StoreProvider } from 'react-redux';
import App from './src/App';
import store from './store';

export default function Main() {
  return (
    <StoreProvider store={store}>
      <PaperProvider>
        <App />
      </PaperProvider>
    </StoreProvider>
  );
}

Customization

You can provide a custom theme to customize the colors, fonts etc. with the Provider component. Check the default theme to see what customization options are supported.

Example:

js
import * as React from 'react';
import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';

const theme = {
  ...DefaultTheme,
  colors: {
    ...DefaultTheme.colors,
    primary: 'tomato',
    accent: 'yellow',
  },
};

export default function Main() {
  return (
    <PaperProvider theme={theme}>
      <App />
    </PaperProvider>
  );
}

Package.Json (`docs/package.json`)

{
"name": "react-native-paper-docs",
"version": "0.0.1",
"description": "Documentation for React Native Paper",
"private": true,
"author": "",
"license": "MIT",
"scripts": {
"start": "component-docs serve",
"build": "component-docs build",
"clean": "del dist/",
"prestart": "yarn clean"
},
"dependencies": {
"color": "^3.1.2",
"component-docs": "^0.23.0",
"linaria": "^1.2.4",
"react-native-vector-icons": "^6.6.0"
},
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/node": "^7.6.3",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-flow": "^7.8.3",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"del-cli": "^3.0.0",
"file-loader": "^4.2.0",
"metro-react-native-babel-preset": "^0.56.0",
"url-loader": "^4.1.0"
}
}


Pages 2.Theming (`docs/pages/2.theming.md`)


title: Theming

Theming

Applying a theme to the whole app

To support custom themes, paper exports a Provider component. You need to wrap your root component with the provider to be able to support themes:

js
import * as React from 'react';
import { Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';

export default function Main() {
  return (
    <PaperProvider>
      <App />
    </PaperProvider>
  );
}

If no prop is specified, this will apply the default theme to the components. You can also provide a theme prop with a theme object with same properties as the default theme:

js
import * as React from 'react';
import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';

const theme = {
  ...DefaultTheme,
  roundness: 2,
  colors: {
    ...DefaultTheme.colors,
    primary: '#3498db',
    accent: '#f1c40f',
  },
};

export default function Main() {
  return (
    <PaperProvider theme={theme}>
      <App />
    </PaperProvider>
  );
}

You can change the theme prop dynamically and all the components will automatically update to reflect the new theme.

A theme usually contains the following properties:

  • dark (boolean): whether this is a dark theme or light theme.
  • mode ('adaptive' | 'exact'): color mode for dark theme (See Dark Theme).
  • roundness (number): roundness of common elements, such as buttons.
  • colors (object): various colors used throughout different elements.
    • primary - primary color for your app, usually your brand color.
    • accent - secondary color for your app which complements the primary color.
    • background - background color for pages, such as lists.
    • surface - background color for elements containing content, such as cards.
    • text - text color for content.
    • disabled - color for disabled elements.
    • placeholder - color for placeholder text, such as input placeholder.
    • backdrop - color for backdrops of various components such as modals.
  • fonts (object): various fonts used throughout different elements.
    • regular
    • medium
    • light
    • thin
  • animation (object)
    • scale - scale for all animations

When creating a custom theme, you will need to provide all of these properties.

If you don't use a custom theme, Paper will automatically turn animations on/off, depending on device settings.

Otherwise, your custom theme will need to handle it manually, using React Native's AccessibilityInfo API.

Extending the theme

Keeping your own properties in the theme is fully supported by our library:

js
import * as React from 'react';
import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';

const theme = {
  ...DefaultTheme,
  // Specify custom property
  myOwnProperty: true,
  // Specify custom property in nested object
  colors: {
    myOwnColor: '#BADA55',
  }
};

export default function Main() {
  return (
    <PaperProvider theme={theme}>
      <App />
    </PaperProvider>
  );
}

TypeScript

By default it won't work well with TypeScript, but we can take advantage of global augmentations and specify the new properties that we added to the theme:

ts
import * as React from 'react';
import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';

declare global {
  namespace ReactNativePaper {
    interface ThemeColors {
      myOwnColor: string;
    }

    interface Theme {
      myOwnProperty: boolean;
    }
  }
}

const theme = {
  ...DefaultTheme,
  // Specify custom property
  myOwnProperty: true,
    // Specify custom property in nested object
  colors: {
    myOwnColor: '#BADA55',
  }
};

export default function Main() {
  return (
    <PaperProvider theme={theme}>
      <App />
    </PaperProvider>
  );
}

As you can see, custom properties e.g. myOwnColor defined in nested object e.g. colors needs to be declared in its own interface. You'll find more information in our example app where we have it implemented.

Applying a theme to a paper component

If you want to change the theme for a certain component from the library, you can directly pass the theme prop to the component. The theme passed as the prop is merged with the theme from the Provider:

js
import * as React from 'react';
import { Button } from 'react-native-paper';

export default function ButtonExample() {
  return (
    <Button raised theme={{ roundness: 3 }}>
      Press me
    </Button>
  );
}

Using the theme in your own components

To access the theme in your own components, you can use the withTheme HOC exported from the library. If you wrap your component with the HOC, you'll receive the theme as a prop:

js
import * as React from 'react';
import { withTheme } from 'react-native-paper';

function MyComponent(props) {
  const { colors } = props.theme;
  return <Text style={{ color: colors.primary }}>Yo!</Text>;
}

export default withTheme(MyComponent);

Components wrapped with withTheme support the theme from the Provider as well as from the theme prop.

You can also use the useTheme hook:

js
import * as React from 'react';
import { useTheme } from 'react-native-paper';

function MyComponent(props) {
  const { colors } = useTheme();
  return <Text style={{ color: colors.primary }}>Yo!</Text>;
}

Customizing all instances of a component

Sometimes you want to style a component in a different way everywhere but don't want to change the properties in the theme so that other components are not affected. For example, say you want to change the font for all your buttons, but don't want to change theme.fonts.medium because it affects other components.

We don't have an API to do this, because you can already do it with components:

js
import * as React from 'react';
import { Button } from 'react-native-paper';

export default function FancyButton(props) {
  return <Button theme={{ fonts: { medium: 'Open Sans' } }} {...props} />;
}

Now you can use your FancyButton component everywhere instead of using Button from Paper.

Dark Theme

Since 3.0 we adapt dark theme to follow Material design guidelines.
In contrast to light theme, dark theme by default uses surface colour instead of primary on large components like AppBar or BottomNavigation.
The dark theme adds a white overlay with opacity depending on elevation of surfaces. It uses it for the better accentuation of surface elevation. Using only shadow is highly imperceptible on dark surfaces.

We are aware that users often use dark theme in their own ways and may not want to use the default dark theme features from the guidelines.
That's why if you are using dark theme you can switch between two dark theme modes:

  • exact where everything is like it was before. Appbar and BottomNavigation will still use primary colour by default.
  • adaptive where we follow Material design guidelines, the surface will use white overlay with opacity to show elevation, Appbar and BottomNavigation will use surface colour as a background.

If you don't use a custom theme, Paper will automatically change between the default theme and the default dark theme, depending on device settings.

Otherwise, your custom theme will need to handle it manually, using React Native's Appearance API.

Gotchas

The Provider exposes the theme to the components via React's context API, which means that the component must be in the same tree as the Provider. Some React Native components will render a different tree such as a Modal, in which case the components inside the Modal won't be able to access the theme. The work around is to get the theme using the withTheme HOC and pass it down to the components as props, or expose it again with the exported ThemeProvider component.

The Modal component from the library already handles this edge case, so you won't need to do anything.


Pages 3.Icons (`docs/pages/3.icons.mdx`)

export const meta = {
title: "Icons"
}

import IconsList from './src/components/IconsList.js'

Icons

Configuring icons

Many of the components require the react-native-vector-icons library to render correctly. If you're using Expo, you don't need to do anything extra, but if it's a vanilla React Native project, you need link the library as described in the getting started guide.

If you opted out of vector icons support using babel-plugin-optional-require, you won't be able to use icon names for the icon prop. Some components may not look correct without vector icons and might need extra configuration.

Using the `icon` prop

Many components such as Button accept an icon prop which is used to display an icon. The icon prop supports the following types of values:

1. An icon name

You can pass the name of an icon from MaterialCommunityIcons. This will use the react-native-vector-icons library to display the icon.

Example:

js
<Button icon="camera">
  Press me
</Button>

See the list of supported icons

2. An image source

You can pass an image source, such as an object of shape { uri: 'https://path.to' } or a local image: require('../path/to/image.png') to use as an icon. The image might be rendered with a different color than the one provided depending on the component. If don't want this behavior, see the next example to pass an Image element.

Remote image:

js
<Button icon={{ uri: 'https://avatars0.githubusercontent.com/u/17571969?v=3&s=400' }}>
  Press me
</Button>

Local image:

js
<Button icon={require('../assets/chameleon.jpg')}>
  Press me
</Button>

3. A render function

You can pass a function which returns a react element to be used an icon. The function receives an object with size and color properties as its arguments. The element is used as is without any modification. However, it might get clipped if the provided element's size is bigger than what the component renders. It's up to you to make sure that the size of the element is correct.

Example:

js
<Button
  icon={({ size, color }) => (
    <Image
      source={require('../assets/chameleon.jpg')}
      style={{ width: size, height: size, tintColor: color }}
    />
  )}
>
  Press me
</Button>

4. Use custom icons

If you want to use icons other than MaterialCommunityIcons you need to import the icons and pass it to the settings prop within PaperProvider.

Example:

js
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
// ...

      <PaperProvider
        settings={{
          icon: props => <AwesomeIcon {...props} />,
        }}
        theme={this.state.theme}
      >
        // ...
      </PaperProvider>

RTL support

If you want your icon to behave properly in a RTL environment, you can pass an object to the icon prop with shape: { source: { uri: 'https://path.to' }, direction : 'rtl' }. source can be any of the values that the icon prop accepts in option 1 and option 2. For direction you have a few options:

  1. auto - uses the device language to determine if icon should be displayed from rtl. Uses the I18nManager module to get this info.
  2. rtl - flips the icon so that it is rtl, this is regardless of the device language.
  3. ltr - displays from ltr, even if in an rtl environment.

Example for using an image source:

js
<Button icon={{ source: { uri: 'https://avatars0.githubusercontent.com/u/17571969?v=3&s=400' }, direction: 'rtl' }}>
  Press me
</Button>

Example for using an icon name:

js
<Button icon={{ source: "add-a-photo", direction: 'rtl' }}>
  Press me
</Button>

You can also use a render function. Along with size and color, you have access to direction which will either be 'rtl' or 'ltr'. You can then decide how to render your icon component accordingly.

Example of using a render function:

js
<Button
  icon={({ size, color, direction }) => (
    <Image
      source={require('../assets/chameleon.jpg')}
      style={[
        {
          transform: [{ scaleX: direction === 'rtl' ? -1 : 1 }],
        },
        {
          width: size,
          height: size,
          tintColor: color
        }
      ]}
    />
  )}
>
  Press me
</Button>

Pages 4.Fonts (`docs/pages/4.fonts.md`)


title: Fonts

Fonts

Installing custom fonts

The easiest way to install custom fonts to your RN project is do as follows:

  1. Define path to assets directory with fonts in project:

Example:

js
// React Native < 0.60 package.json
  ...
    "rnpm": {
      "assets": [
        "fonts"
      ]
    },
  ...

  // React Native >= 0.60 react-native.config.js
  module.exports = {
  ...
  "assets": [
    "fonts"
  ],
  ...

Note: fonts is a folder with .ttf files

  1. Place your font files in your assets folder.
  2. Link font files using 'react-native link' command.
  3. Restart your project to refresh changes.

Now, you are able to use fontFamily from font files.

Configuring fonts in ThemeProvider

To create a custom font, prepare a fontConfig object where fonts are divided by platforms. After that, you have to pass the fontConfig into configureFonts method in a custom theme.

The fontConfig object accepts ios, android, macos, windows, web, and native. Use these to override fonts on particular platforms.

Note: At a minimum, you need to explicitly pass fonts for android, ios, and web.

Check the default theme to see what customization options are supported.

js
import * as React from 'react';
import { configureFonts, DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';

const fontConfig = {
  web: {
    regular: {
      fontFamily: 'sans-serif',
      fontWeight: 'normal',
    },
    medium: {
      fontFamily: 'sans-serif-medium',
      fontWeight: 'normal',
    },
    light: {
      fontFamily: 'sans-serif-light',
      fontWeight: 'normal',
    },
    thin: {
      fontFamily: 'sans-serif-thin',
      fontWeight: 'normal',
    },
  },
  ios: {
    regular: {
      fontFamily: 'sans-serif',
      fontWeight: 'normal',
    },
    medium: {
      fontFamily: 'sans-serif-medium',
      fontWeight: 'normal',
    },
    light: {
      fontFamily: 'sans-serif-light',
      fontWeight: 'normal',
    },
    thin: {
      fontFamily: 'sans-serif-thin',
      fontWeight: 'normal',
    },
  },
  android: {
    regular: {
      fontFamily: 'sans-serif',
      fontWeight: 'normal',
    },
    medium: {
      fontFamily: 'sans-serif-medium',
      fontWeight: 'normal',
    },
    light: {
      fontFamily: 'sans-serif-light',
      fontWeight: 'normal',
    },
    thin: {
      fontFamily: 'sans-serif-thin',
      fontWeight: 'normal',
    },
  }
};

const theme = {
  ...DefaultTheme,
  fonts: configureFonts(fontConfig),
};

export default function Main() {
  return (
    <PaperProvider theme={theme}>
      <App />
    </PaperProvider>
  );
}

Pages 5.React Native Web (`docs/pages/5.react-native-web.md`)


title: Using on the Web

Using on the Web

Pre-requisites

Make sure that you have followed the getting started guide and have react-native-paper installed and configured before following this guide.

We're going to use react-native-web and webpack to use React Native Paper on the web, so let's install them as well.

To install react-native-web, run:

sh
yarn add react-native-web react-dom react-art

Using CRA ([Create React App](https://github.com/facebook/create-react-app))

Install react-app-rewired to override webpack configuration:

sh
yarn add --dev react-app-rewired

Configure babel-loader using a new file called config-overrides.js:

js
module.exports = function override(config, env) {
  config.module.rules.push({
    test: /\.js$/,
    exclude: /node_modules[/\\](?!react-native-vector-icons)/,
    use: {
      loader: "babel-loader",
      options: {
        // Disable reading babel configuration
        babelrc: false,
        configFile: false,

        // The configuration for compilation
        presets: [
          ["@babel/preset-env", { useBuiltIns: "usage" }],
          "@babel/preset-react",
          "@babel/preset-flow",
          "@babel/preset-typescript"
        ],
        plugins: [
          "@babel/plugin-proposal-class-properties",
          "@babel/plugin-proposal-object-rest-spread"
        ]
      }
    }
  });

  return config;
};

Change your script in package.json:

diff
/* package.json */

  "scripts": {
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",
-   "test": "react-scripts test --env=jsdom",
+   "test": "react-app-rewired test --env=jsdom"
}

Custom webpack setup

To install webpack, run:

sh
yarn add --dev webpack webpack-cli webpack-dev-server

If you don't have a webpack config in your project, copy the following to webpack.config.js get started:

js
const path = require('path');

module.exports = {
  mode: 'development',

  // Path to the entry file, change it according to the path you have
  entry: path.join(__dirname, 'App.js'),

  // Path for the output files
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'app.bundle.js',
  },

  // Enable source map support
  devtool: 'source-map',

  // Loaders and resolver config
  module: {
    rules: [

    ],
  },
  resolve: {

  },

  // Development server config
  devServer: {
    contentBase: [path.join(__dirname, 'public')],
    historyApiFallback: true,
  },
};

Also create a folder named public and add the following file named index.html:

html
<!doctype html>
<head>
  <meta charSet="utf-8" />
  <meta httpEquiv="X-UA-Compatible" content="IE=edge" />

  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />

  <title>App</title>

  <style>
    html, body, #root {
      height: 100%;
    }

    #root {
      display: flex;
      flex-direction: column;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script src="app.bundle.js"></script>
</body>

Now we're ready to start configuring the project.

Configure webpack

1. Alias `react-native` to `react-native-web`

First, we have to tell webpack to use react-native-web instead of react-native. Add the following alias in your webpack config under resolve:

js
alias: {
  'react-native$': require.resolve('react-native-web'),
}

2. Configure `babel-loader`

Next, we want to tell babel-loader to compile react-native-paper and react-native-vector-icons. We would also want to disable reading the babel configuration files to prevent any conflicts.

First install the required dependencies:

sh
yarn add --dev babel-loader @babel/preset-env @babel/preset-react @babel/preset-flow @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread

Now, add the following in the module.rules array in your webpack config:

js
{
  test: /\.js$/,
  exclude: /node_modules[/\\](?!react-native-vector-icons)/,
  use: {
    loader: 'babel-loader',
    options: {
      // Disable reading babel configuration
      babelrc: false,
      configFile: false,

      // The configuration for compilation
      presets: [
        ['@babel/preset-env', { useBuiltIns: 'usage' }],
        '@babel/preset-react',
        '@babel/preset-flow',
        "@babel/preset-typescript"
      ],
      plugins: [
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-proposal-object-rest-spread'
      ],
    },
  },
},

3. Configure `file-loader`

To be able to import images and other assets using require, we need to configure file-loader. Let's install it:

sh
yarn add --dev file-loader

To configure it, add the following in the module.rules array in your webpack config:

js
{
  test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
  loader: 'file-loader',
}

Load the Material Community Icons

If you followed the getting started guide, you should have the following code in your root component:

js
<PaperProvider>
  <App />
</PaperProvider>

Now we need tweak this section to load the Material Community Icons from the react-native-vector-icons library:

js
<PaperProvider>
  <React.Fragment>
    {Platform.OS === 'web' ? (
      <style type="text/css">{`
        @font-face {
          font-family: 'MaterialCommunityIcons';
          src: url(${require('react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf')}) format('truetype');
        }
      `}</style>
    ) : null}
    <App />
  </React.Fragment>
</PaperProvider>

Remember to import Platform from react-native at the top:

js
import { Platform } from 'react-native';

You can also load these fonts using css-loader if you prefer.

Load the Roboto fonts (optional)

The default theme in React Native Paper uses the Roboto font. You can add them to your project following the instructions on its Google Fonts page.

We're done!

You can run webpack-dev-server to run the webpack server and open your project in the browser. You can add the following script in your package.json under the "scripts" section to make it easier:

json
"web": "webpack-dev-server --open"

Now you can run yarn web to run the project on web.



Our mission is to provide a full suite of well-integrated components built with Material Design in mind. However, many components already have better well maintained implementations.

Here are some of the libraries we recommend:

Tabs

react-native-community/react-native-tab-view

Material Design themed swipeable tabs, maintained by @satya164 and @mosdnk.

Bottom sheet

osdnk/reanimated-bottom-sheet

An implementation bottom sheet behaviour, maintained by @mosdnk.

Date Picker

web-ridge/react-native-paper-dates
react-native-community/react-native-datetimepicker

Time Picker

web-ridge/react-native-paper-dates


Pages 7.Contributing (`docs/pages/7.contributing.md`)


title: Contributing

/../../CONTRIBUTING.md


Pages 8.Theming With React Navigation (`docs/pages/8.theming-with-react-navigation.md`)


title: Theming with React Navigation

Theming with React Navigation

In this guide we will look into how to apply theming for an application using React Native Paper and React Navigation at the same time.

Offering different theme options, especially dark/light ones, becomes increasingly a standard requirement of the modern mobile application. Fortunately, both React Navigation and React Native Paper support configurable theming out-of-the-box.
But how to make them work together?

Combining theme objects

Both libraries require a wrapper to be used at the entry point of the application.
React Navigation exposes NavigationContainer which ensures that navigation works correctly, but also accepts theme as an optional property. Read more about setting up navigation here.
For React Native Paper theme to work, we need to use PaperProvider also at application's entry point.

js
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { TouchableOpacity } from 'react-native';
import {
  Card,
  Title,
  Paragraph,
  List,
  Provider as PaperProvider,
} from 'react-native-paper';

const Stack = createStackNavigator();

const HomeScreen = ({ navigation }) => (
  <TouchableOpacity
    onPress={() =>
      navigation?.push('Details', {
        title,
        content,
      })
    }
  >
    <Card>
      <Card.Content>
        <Title>{title}</Title>
        <Paragraph>{content}</Paragraph>
      </Card.Content>
    </Card>
  </TouchableOpacity>
);

const DetailsScreen = (props) => {
  const { title, content } = props?.route?.params;
  return (
    <List.Section>
      <List.Subheader>{title}</List.Subheader>
      <List.Item title={content} />
    </List.Section>
  );
};

export default function App() {
  return (
    <PaperProvider>
      <NavigationContainer>
        <Stack.Navigator initialRouteName="Home">
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Details" component={DetailsScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    </PaperProvider>
  );
}

Fortunately, both React Navigation and React Native Paper offer very similar API when it comes to theming. It's possible to import default themes in light and dark variants from both.

React Navigation and React Native Paper use the same name for default themes - DefaultTheme and DarkTheme, so we need to alias them at the imports.

Our goal here is to combine those two themes, so that we could control the theme for the entire application from a single place.

To make things easier we can use deepmerge package. With yarn we can install it like this

sh
yarn add deepmerge
js
import {
  NavigationContainer,
  DarkTheme as NavigationDarkTheme,
  DefaultTheme as NavigationDefaultTheme,
} from '@react-navigation/native';
import {
  DarkTheme as PaperDarkTheme,
  DefaultTheme as PaperDefaultTheme,
  Provider as PaperProvider,
} from 'react-native-paper';
import merge from 'deepmerge';

const CombinedDefaultTheme = merge(PaperDefaultTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);

Alternatively, we could merge those themes using vanilla JavaScript

js
const CombinedDefaultTheme = {
  ...PaperDefaultTheme,
  ...NavigationDefaultTheme,
  colors: {
    ...PaperDefaultTheme.colors,
    ...NavigationDefaultTheme.colors,
  },
};
const CombinedDarkTheme = {
  ...PaperDarkTheme,
  ...NavigationDarkTheme,
  colors: {
    ...PaperDarkTheme.colors,
    ...NavigationDarkTheme.colors,
  },
};

Passing theme with Providers

After combining the themes, we will be able to control theming in both libraries from a single source, which will come in handy later.

Next, we need to pass merged themes into the Providers. For this part, we use the dark one - CombinedDarkTheme.

js
const CombinedDefaultTheme = merge(PaperDefaultTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);

const Stack = createStackNavigator();

export default function App() {
  return (
    <PaperProvider theme={CombinedDarkTheme}>
      <NavigationContainer theme={CombinedDarkTheme}>
        <Stack.Navigator initialRouteName="Home">
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Details" component={DetailsScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    </PaperProvider>
  );
}

Customizing theme

We don't need to limit ourselves to the themes offered by the libraries in default. Both packages allow for custom themes to be applied.
You can learn all about it their documentations:

React Context for theme customization

Now, we wouldn't want to stay forever with dark theme being on, which is why we need to gain the ability to control theme dynamically. A bit of state management is needed for this purpose.

React Context proves itself very useful in handling cross-cutting concerns like global theme handling, so we will use just that.

Creating Context

First, we define our Context.

js
import React from 'react';

export const PreferencesContext = React.createContext({
  toggleTheme: () => {},
  isThemeDark: false,
});

Using Context

Context Provider should be imported also at the entry point, as we want it to wrap the whole app, for the theme values to be accessible at every component that we have.

js
import React from 'react';
import { PreferencesContext } from './PreferencesContext';

const Stack = createStackNavigator();

const CombinedDefaultTheme = merge(PaperDefaultTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);

export default function App() {
  const [isThemeDark, setIsThemeDark] = React.useState(false);

  let theme = isThemeDark ? CombinedDarkTheme : CombinedDefaultTheme;

  const toggleTheme = React.useCallback(() => {
    return setIsThemeDark(!isThemeDark);
  }, [isThemeDark]);

  const preferences = React.useMemo(
    () => ({
      toggleTheme,
      isThemeDark,
    }),
    [toggleTheme, isThemeDark]
  );

  return (
    // Context is wired into the local state of our main component, so that its values could be propagated throughout the entire application
    <PreferencesContext.Provider value={preferences}>
      <PaperProvider theme={theme}>
        <NavigationContainer theme={theme}>
          <Stack.Navigator initialRouteName="Home">
            <Stack.Screen name="Home" component={HomeScreen} />
            <Stack.Screen name="Details" component={DetailsScreen} />
          </Stack.Navigator>
        </NavigationContainer>
      </PaperProvider>
    </PreferencesContext.Provider>
  );
}

Now that the Context is available at every component, all we need to do is import it. Next thing is to provide the user with some UI element to control changing the theme. We will use Paper's Switch for this purpose.

js
import React from 'react';
import { useTheme, Appbar, TouchableRipple, Switch } from 'react-native-paper';
import { PreferencesContext } from './PreferencesContext';

const Header = ({ scene }) => {
  const theme = useTheme();
  const { toggleTheme, isThemeDark } = React.useContext(PreferencesContext);

  return (
    <Appbar.Header
      theme={{
        colors: {
          primary: theme?.colors.surface,
        },
      }}
    >
      <Appbar.Content title={scene.route?.name} />
      <TouchableRipple onPress={() => toggleTheme()}>
        <Switch
          style={[{ backgroundColor: theme.colors.accent }]}
          color={'red'}
          value={isThemeDark}
        />
      </TouchableRipple>
    </Appbar.Header>
  );
};

And now you can switch between light and dark theme!

Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state.

React Native Paper components will automatically use provided theme thanks to the PaperProvider that is wrapped around the entry point of our application, but we can also access theme values manually with useTheme hook,
exposed by the library. You can see how it's done in the Header component code above.

If light/dark themes are not enough for your use case, you can learn more about creating Material Design themes here.
On master branch of the example app, you will find implemented Menu component, which allows to choose a few custom themes. Inspecting code in utils and Header may give you some idea how to use your own themes with Paper, in addition to dedicated docs.

Read more about integrating Paper with React Navigation in a brilliant article by @trensik


Pages 9.React Navigation (`docs/pages/9.react-navigation.md`)


title: Integrate AppBar with react-navigation

Integrate AppBar with react-navigation

Prerequisites

  • react-native-paper
  • react-navigation

We assume that you have already installed the mentioned libraries above, otherwise please check out the guides below.

React Native Paper - Getting Started

React Navigation - Getting Started

Stack Navigator

We will start with react-navigation by creating a basic navigation stack. Stack navigator gives us a possibility of transition between screens in our app and manage navigation's history. In a simple scenario where there is only one stack navigator present in the app, it resembles a navigation state in a browser.
Screens are pushed and popped from the stack while the user navigates to a new screen or go back to the previous one.

Let's create two screens. A main screen named Home and details screen named Details.

js
import 'react-native-gesture-handler';
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

At the moment our navigation stack contains two screens and will render HomeScreen or DetailsScreen components according to the current navigation state. We have not implemented those components yet, so let's do this now:

js
import React from 'react';
import {View, Text, Button, StyleSheet} from 'react-native';

function HomeScreen() {
  return (
    <View style={style.container}>
      <Text>Home Screen</Text>
    </View>
  );
}

function DetailsScreen() {
  return (
    <View style={style.container}>
      <Text>Details Screen</Text>
    </View>
  );
}

const style = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Once we have finished implementing the components, we can run the app and check how Stack looks like.

To navigate from HomeScreen to DetailsScreen we can use the navigation object provided by Stack.Screen component. Every component rendered by Stack.Screen has an access to the navigation object via props. Let's modify our HomeScreen component:

js
function HomeScreen({ navigation }) {
  return (
    <View style={style.container}>
      <Text>Home Screen</Text>
      <Button
        title="Go to details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

Our result:

As you can see, we can already navigate between two screens. In the next steps, we will show you how to use Paper's AppBar instead of the default header.

Adding AppBar

We can customize Stack's header by passing custom component:

js
export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={{
          header: CustomNavigationBar,
        }}>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Now we will implement CustomNavigationBar using AppBar component:

js
import { Appbar } from 'react-native-paper';

function CustomNavigationBar() {
  return (
    <Appbar.Header>
      <Appbar.Content title="My awesome app" />
    </Appbar.Header>
  );
}

Current implementation of the CustomNavigationBar is simple - we just render a title inside of it. You may notice there is no way to go back to the previous screen, because the back button is not visible in the header. Let's add it now and let's make sure it's visible on all Stack's screens except Home screen.

Firstly, pass navigation props to CustomNavigationBar:

js
export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={{
          header: (props) => <CustomNavigationBar {...props} />,
        }}>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Secondly, we check if navigation has previous state. If it has, it means there is another screen on the stack beneath the current screen and we should render the back arrow button in such a case.

js
function CustomNavigationBar({ navigation, previous }) {
  return (
    <Appbar.Header>
      {previous ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
      <Appbar.Content title="My awesome app" />
    </Appbar.Header>
  );
}

Another interesting pattern that can be implemented with react-native-paper and react-navigation is a "hamburger menu". Thanks to the Menu component we can add a nice looking pop-up to our Appbar. To implement this feature we need to make a couple of changes in CustomNavigationBar:

  • Render a Menu component
  • Pass Appbar.Action to the anchor prop
  • Add a state to control Menu visibility

We also want the menu to appear only on HomeScreen, which means we will render it conditionally based on the previous prop.

js
function CustomNavigationBar({ navigation, previous }) {
  const [visible, setVisible] = React.useState(false);
  const openMenu = () => setVisible(true);
  const closeMenu = () => setVisible(false);

  return (
    <Appbar.Header>
      {previous ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
      <Appbar.Content title="My awesome app" />
      {!previous ? (
        <Menu
          visible={visible}
          onDismiss={closeMenu}
          anchor={
            <Appbar.Action icon="menu" color="white" onPress={openMenu} />
          }>
          <Menu.Item onPress={() => {console.log('Option 1 was pressed')}} title="Option 1" />
          <Menu.Item onPress={() => {console.log('Option 2 was pressed')}} title="Option 2" />
          <Menu.Item onPress={() => {console.log('Option 3 was pressed')}} title="Option 3" disabled />
        </Menu>
      ) : null}
    </Appbar.Header>
  );
}

Final result:

That's all we need! We have app bar that contains everything we need to navigate through screens and access an additional menu on the main screen. As you can see, with Material design Appbar provided by react-native-paper used together with react-navigation we can easily create an app that looks and works great.


--- METRICS ---