Creating a Full Screen Pressable Overlay From a Bottom Tab Navigator in React Native
Tags: reactnative, react
Published: 2021-11-08 12:51:41

---
title: Creating a Full Screen Pressable Overlay From a Bottom Tab Navigator in React Native
published: true
date: 2021-11-08 00:00:00 UTC
tags: reactnative, react
description: That works with Android
canonical_url: https://bionicjulia.com/blog/full-screen-pressable-overlay-from-bottom-tab-navigator-react-native
---

I came across a React Native issue last week, which initially got me rather confused as I thought it had to do with z-index positioning on Android. Turns out, there's an identified RN issue to do specifically, with how `Pressable` child components are rendered on Android. Scroll to the bottom of [this page](https://reactnative.dev/docs/handling-touches) if you're interested in more specifics.

To provide more context on my specific use case, I decided to try my hand at some diagrams. It's not the best, but hopefully does the job. 😛 If anyone has tips on how to export hand-drawn diagrams on an iPad to a Mac laptop, let me know.

### The problem

Picture a mobile screen. At the bottom of the screen, I've got a [React Navigation bottom tab navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).

![Mobile screen with tab navigator](https://user-images.githubusercontent.com/7389546/140657841-f3562346-256d-447c-84ef-8bd7aba60d27.png)

What I wanted was to have a darker overlay appear over the whole screen which would disappear upon being pressed i.e. a `Pressable` overlay. Because of some other side effects, the trigger for showing / hiding the overlay needed to be done from the tab navigator.

![Pressable overlay](https://user-images.githubusercontent.com/7389546/140657843-85b791bc-9c77-46d1-8bbe-7f12c17e84bc.png)

To do this, I used the `<Tab.Screen>` `options` prop to pass in a custom `tabBarIcon`, that includes not just the icon for the tab, but also a conditional `Pressable` overlay which looks something like this... (note that I set the height and width of the overlay to take the full screen using the `useWindowDimensions` hook from `react-native`).

```jsx
export default function TabNavigator() {
  const windowDimensions = useWindowDimensions()
  const [showOverlay, setShowOverlay] = React.useState < boolean > false

  const tabScreenOptions = {
    // other options
    tabBarIcon: ({ focused, color }: { focused: boolean, color: string }) => {
      return (
        <>
          // tab icon component
          {showOverlay ? (
            <Pressable
              onPress={closeOverlay}
              style={[styles.overlay, { height: windowDimensions.height, width: windowDimensions.width }]}
            />
          ) : null}
        </>
      )
    },
  }

  return (
    <Tab.Navigator>
      // Other tab screens
      <Tab.Screen component={TabScreen} name="Tab name" options={tabScreenOptions} />
    </Tab.Navigator>
  )
}

const styles = StyleSheet.create({
  overlay: {
    backgroundColor: 'black',
    flex: 1,
    opacity: 0.5,
    position: 'absolute',
    zIndex: 1,
  },
})
```

When testing on iOS, all works as expected. Upon the `showOverlay` state variable being set to `true`, the full screen overlay appears and is pressable. 🎉

![iOS pressable area](https://user-images.githubusercontent.com/7389546/140657844-26bf513a-9f36-4a3b-9689-9ab12d5cf0ae.png)

Android does not work as expected however, because Android only allows the overlapping area of the child (i.e. the overlay) and parent (tab navigator) to be pressable. In my case, because the tab navigator is smaller than the full screen overlay, I'm stuck. In order to mimic the behaviour seen in iOS, I'd need to make the tab navigator height and width to be that of the entire screen... which isn't a viable option in my case.

![Android pressable area](https://user-images.githubusercontent.com/7389546/140657846-5b69e064-e20c-4109-9163-fd9f47579013.png)

### A potential solution

So what did I do? I decided to go down a slightly convoluted path in order to get more flexibility by using [React context](https://reactjs.org/docs/context.html). As the tab navigator acts as the parent for all the screens and any child components present within the screens, I could set up a **context provider** at the tab navigator level and then put in place **context consumers** at whatever component I needed down the hierachy.

```jsx
export function TabNavigator() {
  const [showOverlay, setShowOverlay] = React.useState < boolean > false
  export const OverlayContext = React.createContext({ closeOverlay: () => {}, showOverlay: false })

  // const tabScreenOptions same as before

  const overlayContext = {
    closeOverlay,
    showOverlay,
  }

  const closeOverlay = React.useCallback(async () => {
    setShowOverlay(false)
    // do some other things
  }, [])

  return (
    <OverlayContext.Provider value={overlayContext}>
      <Tab.Navigator>
        // Other tab screens
        <Tab.Screen component={TabScreen} name="Tab name" options={tabScreenOptions} />
      </Tab.Navigator>
    </OverlayContext.Provider>
  )
}
```

To set up a consumer, in any child component of the `TabNavigator`, I used the `useContext` hook.

```jsx
export function ChildComponent() {
  const overlayContext = React.useContext(OverlayContext)

  return (
    <>
      {Platform.OS === 'android' && overlayContext.showOverlay ? (
        <Pressable
          onPress={overlayContext.closeOverlay}
          style={[styles.overlay, { height: windowDimensions.height, width: windowDimensions.width }]}
        />
      ) : null}
      // the child component
    </>
  )
}
```

To be specific, I used the `Platform` module from `react-native` to check that this only shows up for Android operating systems. The `styles.overlay` styling is the same as what I had previously.

Let me know what you think! I blog at https://bionicjulia.com and can be found on [Twitter](https://twitter.com/bionicjulia) and [Instagram](https://instagram.com/bionicjulia).