react-native-edge-to-edge

repository·main·Indexed 21 days ago

https://github.com/zoontek/react-native-edge-to-edge

A library to enable edge-to-edge display in React Native Android apps (v6+), allowing content to flow behind system status and navigation bars. It provides a <SystemBars /> component to replace deprecated StatusBar APIs and a stack-based API for managing bar styles. Includes an Expo config plugin and a companion utility, react-native-is-edge-to-edge, for library authors to detect edge-to-edge status.

Tokens
4.7K
Snippets
13
Records
22
Agent score
75%

What's inside react-native-edge-to-edge

  1. Install react-native-edge-to-edge

    main

    Install the library using npm or yarn to enable edge-to-edge display on Android (v6 and above), allowing content to flow beneath system bars.

    IMPORTANT

    If you are using React Native 0.86 or later, it is recommended to use the built-in edgeToEdgeEnabled=true Gradle property instead of this library. You can pair that with @zoontek/react-native-navigation-bar for navigation bar styling.

    $ npm i -S react-native-edge-to-edge
    # --- or ---
    $ yarn add react-native-edge-to-edge
  2. Use react-native-is-edge-to-edge to support react-native-edge-to-edge

    main

    The react-native-is-edge-to-edge package is a lightweight utility designed for library authors to detect if react-native-edge-to-edge is installed in the host application. This allows libraries to automatically adjust their status bar and navigation bar translucency settings to prevent interference with the edge-to-edge layout.

    To use it, you can check the edge-to-edge status using isEdgeToEdge() and use controlEdgeToEdgeValues in development mode to warn users if they are providing redundant translucency props that might conflict with the edge-to-edge configuration.

    import {
      controlEdgeToEdgeValues,
      isEdgeToEdge,
      isEdgeToEdgeFromLibrary,
      isEdgeToEdgeFromProperty,
    } from "react-native-is-edge-to-edge";
    
    const EDGE_TO_EDGE = isEdgeToEdge();
    
    function MyAwesomeLibraryComponent({
      statusBarTranslucent,
      navigationBarTranslucent,
    }) {
      if (__DEV__) {
        // warn the user once about unnecessary defined values
        controlEdgeToEdgeValues({
          statusBarTranslucent,
          navigationBarTranslucent,
        });
      }
    
      return (
        <MyAwesomeLibraryNativeComponent
          statusBarTranslucent={EDGE_TO_EDGE || statusBarTranslucent}
          navigationBarTranslucent={EDGE_TO_EDGE || navigationBarTranslucent}
          // …
        />
      );
    }
  3. Configure Android AppTheme for edge-to-edge

    main

    To use this library, you must update your Android AppTheme to inherit from an edge-to-edge version. Choose the replacement theme based on your current theme:

    Current ThemeNew Edge-to-Edge Theme
    Theme.AppCompat.DayNight.NoActionBarTheme.EdgeToEdge
    Theme.MaterialComponents.DayNight.NoActionBarTheme.EdgeToEdge.Material2
    Theme.Material3.DayNight.NoActionBarTheme.EdgeToEdge.Material3
    Theme.Material3.DynamicColors.DayNight.NoActionBarTheme.EdgeToEdge.Material3.Dynamic
    Theme.Material3Expressive.DayNight.NoActionBarTheme.EdgeToEdge.Material3Expressive
    Theme.Material3Expressive.DynamicColors.DayNight.NoActionBarTheme.EdgeToEdge.Material3Expressive.Dynamic
    Theme.AppCompat.Light.NoActionBarTheme.EdgeToEdge.Light
    Theme.MaterialComponents.Light.NoActionBarTheme.EdgeToEdge.Material2.Light
    Theme.Material3.Light.NoActionBarTheme.EdgeToEdge.Material3.Light
    Theme.Material3.DynamicColors.Light.NoActionBarTheme.EdgeToEdge.Material3.Dynamic.Light
    Theme.Material3Expressive.Light.NoActionBarTheme.EdgeToEdge.Material3Expressive.Light
    Theme.Material3Expressive.DynamicColors.Light.NoActionBarTheme.EdgeToEdge.Material3Expressive.Dynamic.Light
  4. Configure Bare React Native with styles.xml

    main

    In a bare React Native project, edit android/app/src/main/res/values/styles.xml to update your AppTheme parent and optionally disable navigation bar contrast.

    <resources>
      <!-- update your AppTheme parent -->
      <style name="AppTheme" parent="Theme.EdgeToEdge">
        <!-- disable the contrasting background of the navigation bar (optional) -->
        <item name="enforceNavigationBarContrast">false</item>
      </style>
    </resources>
  5. Configure Expo with react-native-edge-to-edge

    main

    Add the library plugin to your Expo configuration. Note that these settings require a development build and are not supported in Expo Go.

    Dynamic configuration (app.config.js, app.config.ts)

    import type { ConfigContext, ExpoConfig } from "expo/config";
    import edgeToEdge from "react-native-edge-to-edge/expo";
    
    export default ({ config }: ConfigContext): ExpoConfig => ({
      // …
      plugins: [
        edgeToEdge({
          android: {
            parentTheme: "Default",
            enforceNavigationBarContrast: false,
          },
        }),
      ],
    });

    Static configuration (app.json)

    {
      "expo": {
        "plugins": [
          [
            "react-native-edge-to-edge",
            {
              "android": {
                "parentTheme": "Default",
                "enforceNavigationBarContrast": false
              }
            }
          ]
        ]
      }
    }
  6. Troubleshoot opaque system bars

    main

    If your system bars remain opaque instead of becoming transparent/edge-to-edge, check the following:

    1. Update Ecosystem Dependencies: Ensure you are using the latest versions of libraries that interact with system bars. Key libraries include:

      • expo-router
      • galeria
      • react-native-avoid-softinput
      • react-native-bootsplash
      • react-native-bottom-tabs
      • react-native-keyboard-controller
      • react-native-reanimated
      • react-native-screens
      • react-native-true-sheet
      • react-native-unistyles (v3)
    2. Replace Built-in Components: You must replace all instances of the built-in React Native StatusBar, expo-status-bar, and expo-navigation-bar with the SystemBars component provided by this library.

  7. Manage SystemBars with the Stack API

    main

    The SystemBars API provides imperative methods to manage bar styles using a stack-based approach. This is useful for changing bar styles when navigating between screens.

    • SystemBars.pushStackEntry(props): Pushes a new configuration onto the stack. Returns a SystemBarsEntry which must be passed to popStackEntry to revert.
    • SystemBars.popStackEntry(entry): Removes the specified entry from the stack.
    • SystemBars.replaceStackEntry(entry, props): Replaces an existing entry with new properties.
    • SystemBars.setStyle(style): Directly sets the system bars style.
    • SystemBars.setHidden(hidden): Directly shows or hides the system bars.
  8. Use the SystemBars component

    main

    The <SystemBars /> component replaces standard StatusBar or expo-status-bar components to avoid using deprecated APIs in an edge-to-edge layout. It manages the appearance and visibility of both status and navigation bars.

    Props

    • style: Sets the color of the system bar content. Can be a single value or an object specifying statusBar and navigationBar separately.
      • Values: "auto", "inverted", "light", "dark".
    • hidden: Hides the system bars. Can be a boolean or an object specifying statusBar and navigationBar separately.

    Example

    import { SystemBars } from "react-native-edge-to-edge";
    
    const App = () => (
      <>
        <SystemBars style="light" />
        {/* … */}
      </>
    );
    import { SystemBars } from "react-native-edge-to-edge";
    
    // "auto" is based on current color scheme (light -> dark content, dark -> light content)
    type Style = "auto" | "inverted" | "light" | "dark";
    
    type SystemBarsProps = {
      // set the color of the system bar content (as no effect on semi-opaque navigation bar)
      style?: Style | { statusBar?: Style; navigationBar?: Style };
      // hide system bars (the navigation bar cannot be hidden on iOS)
      hidden?: boolean | { statusBar?: boolean; navigationBar?: boolean };
    };
    
    const App = () => (
      <>
        <SystemBars style="light" />
        {/* … */}
      </>
    );
  9. Configure Metro for the example project

    main

    The example/metro.config.js file uses a specific configuration to handle peer dependencies in a monorepo-like structure. It ensures that only one version of each peer dependency is loaded by blocking the versions located in the root node_modules and aliasing them to the versions found in the example/node_modules directory via extraNodeModules and blockList.

    const path = require("path");
    const pkg = require("../package.json");
    const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
    const escape = require("escape-string-regexp");
    
    const peerDependencies = Object.keys(pkg.peerDependencies);
    const root = path.resolve(__dirname, "..");
    const projectNodeModules = path.join(__dirname, "node_modules");
    const rootNodeModules = path.join(root, "node_modules");
    
    // Block root peer dependencies to prevent multiple versions being loaded
    const blockList = peerDependencies.map(
      (name) => new RegExp(`^${escape(path.join(rootNodeModules, name))}\\/.*$"`),
    );
    
    // Alias peer dependencies to the local example node_modules
    const extraNodeModules = peerDependencies.reduce((acc, name) => {
      return { ...acc, [name]: path.join(projectNodeModules, name) };
    }, {});
    
    const config = {
      projectRoot: __dirname,
      watchFolders: [root],
      resolver: { blockList, extraNodeModules },
    };
    
    module.exports = mergeConfig(getDefaultConfig(__dirname), config);
  10. Configure Expo Plugin Options

    main

    When using the Expo plugin, you can configure the following options for Android:

    • parentTheme: The base theme to inherit from. Available values:
      • "Default" (uses Theme.EdgeToEdge)
      • "Material2" (uses Theme.EdgeToEdge.Material2)
      • "Material3" (uses Theme.EdgeToEdge.Material3)
      • "Material3.Dynamic" (uses Theme.EdgeToEdge.Material3.Dynamic)
      • "Material3Expressive" (uses Theme.EdgeToEdge.Material3Expressive)
      • "Material3Expressive.Dynamic" (uses Theme.EdgeToEdge.Material3Expressive.Dynamic)
      • "Light" (uses Theme.EdgeToEdge.Light)
      • "Material2.Light" (uses Theme.EdgeToEdge.Material2.Light)
      • "Material3.Light" (uses Theme.EdgeToEdge.Material3.Light)
      • "Material3.Dynamic.Light" (uses Theme.EdgeToEdge.Material3.Dynamic.Light)
      • "Material3Expressive.Light" (uses Theme.EdgeToEdge.Material3Expressive.Light)
      • "Material3Expressive.Dynamic.Light" (uses Theme.EdgeToEdge.Material3Expressive.Dynamic.Light)
    • enforceNavigationBarContrast: A boolean to determine if the navigation bar contrast should be enforced. Defaults to true. Setting this to false enables full transparency but requires manual management of the navigation bar style via SystemBars.