Taofiq Aiyelabegan Taofiq is a technical writer and software engineer working on building mobile and web applications. Additionally, he likes to contribute to open source projects and exploring new topics.

How to create customized pop-up menus in React Native

7 min read 2182

How To Create Customized Pop-Up Menus In React Native

Pop-up menus have become an important and common part of mobile applications today. A pop-up menu can be handy when we need to provide some menu options after a button is clicked, mainly so we don’t have to use an extra drawer navigator.

A pop-up menu is usually shown at the top of the element that’s triggering it, as shown below in this example from Twitter:

Twitter Example Of A Pop-Up Menu

Pop-up menus are a great way to improve the UI of your React Native application. They provide an overflow menu as shown above, and these can be used as a secondary action for an item or a button.

In this article, we will be exploring how to create pop-up menus in React Native with different examples. We will create a basic pop-up menu, a custom pop-up menu with icons and dividers, as well as a pop-up menu with a scroll view (including icons and dividers as well).

Setting up our environment

To set up the development environment, we will be using Expo to bootstrap our project. First, we will run this command in the terminal:

npx create-expo-app rn-popup-menu 

This will create an Expo project with the name rn-popup-menu.

Moving on, we will install the needed library for creating the pop-up menu. This library is react-native-popup-menu, found on its GitHub repo here:

npm install react-native-popup-menu --save

To start the project in the simulator/emulator, we will run the command npx expo start and select i for iOS and a for Android. Now, we should have this in the iOS simulator:

Starting UI Screen Open App.js To Get Started On Your App

Next, we will create a folder named components. This is where we will create our different pop-up menu examples.

Creating a basic pop-up menu in React Native

To create a basic pop-up menu that’s triggered by the click of a button, we will create a file called SimpleMenu.js in our component folder. The component will then be rendered in the App.js file:

// SimpleMenu.js
import { StyleSheet, Text, View } from "react-native";
import React from "react";

const SimpleMenu = () => {
 return (
   <View style={styles.container}>
     <Text>SimpleMenu</Text>
   </View>
 );
};

export default SimpleMenu;

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#fff",
   justifyContent: "center",
   alignItems: "center",
 },
});
// App.js
import { StyleSheet, Text, View } from "react-native";
import SimpleMenu from "./components/SimpleMenu";

export default function App() {
 return (
   <View style={styles.container}>
     <SimpleMenu />
   </View>
 );
}

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#fff",
 },
});

Now, this will be rendered in our simulator/emulator:

Simple Menu Ui

Next, to begin with the pop-up menu implementation, we’ll take a look at some of the inbuilt APIs for this library:

  • MenuProvider: this is an API that provides the methods to handle the pop-up menus imperatively
  • Menu: serves as the root menu component used to define menu names and other necessary events
  • MenuTrigger: allows us to define the position of the displaying pop-up menu
  • MenuOptions: encloses all the menu options
  • MenuOption: a wrapper component for all menu options

You can read more about these APIs here. Now, we will make use of these APIs to create a SimpleMenu.js file:

// SimpleMenu.js
import { StyleSheet, Text, View } from "react-native";
import React from "react";
import {
 Menu,
 MenuProvider,
 MenuOptions,
 MenuOption,
 MenuTrigger,
} from "react-native-popup-menu";

const SimpleMenu = () => {
 return (
   <MenuProvider style={styles.container}>
     <Menu>
       <MenuTrigger
         text="Click for Option menu"
         customStyles={{
           triggerWrapper: {
             top: -20,
           },
         }}
       />
       <MenuOptions>
         <MenuOption onSelect={() => alert(`Save`)} text="Save" />
         <MenuOption onSelect={() => alert(`Delete`)} text="Delete" />
       </MenuOptions>
     </Menu>
   </MenuProvider>
 );
};

export default SimpleMenu;

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#fff",
   justifyContent: "center",
   alignItems: "center",
   padding: 30,
   flexDirection: "column",
 },
});

Here, we are creating a simple pop-up menu with the above APIs. MenuProvider wraps all the menu content. In MenuTrigger, we are passing the text option that we want to be displayed, which is Click for Option menu.

We are also using custom styles to define the MenuTrigger content style. MenuOptions has two MenuOption children; these are what will pop up when the MenuTrigger is clicked.

Lastly, the onSelect event triggers the menu option when it’s selected. We’ll display an alert message when we select each menu option:

MenuTrigger and MenuOption can have personalized children, i.e. we can define the child structure the way we want alongside its styling. Let us use an icon in place of the text (Click for Option menu) for the MenuTrigger:

// SimpleMenu.js
import {
 Menu,
 MenuProvider,
 MenuOptions,
 MenuOption,
 MenuTrigger,
} from "react-native-popup-menu";
import { Entypo } from "@expo/vector-icons";

const SimpleMenu = () => {
 return (
   <MenuProvider style={styles.container}>
     <Menu>
       <MenuTrigger
         customStyles={{
           triggerWrapper: {
             top: -20,
           },
         }}
       >
         <Entypo name="dots-three-vertical" size={24} color="black" />
       </MenuTrigger>
       <MenuOptions>
         <MenuOption onSelect={() => alert(`Save`)} text="Save" />
         <MenuOption onSelect={() => alert(`Delete`)} text="Delete" />
       </MenuOptions>
     </Menu>
   </MenuProvider>
 );
};

export default SimpleMenu;

Now, we will have a menu icon displayed in our emulator/simulator that can be used to trigger the menu options:

Simple Menu Icon Three Vertical Dors

Creating custom pop-up menu dividers and options in React Native

We can take the pop-up menu that we created a step forward, just like the example menu we displayed at the beginning of the article. To do this, we’ll customize the MenuTrigger and MenuOption to have a text divider and icon.

For this, we will create two new files CustomMenu.js and CustomContents.js. In CustomMenu.js, we will import the APIs as normal, create an icon for the MenuTrigger, and give it customStyles.

For MenuOptions, we will have six options that will be displayed. Each option will be a component created in CustomContents.js:

// CustomContents.js 
import { MenuOption } from "react-native-popup-menu";
import { Text } from "react-native";
import { Entypo } from "@expo/vector-icons";
import { SimpleLineIcons } from "@expo/vector-icons";
import { EvilIcons } from "@expo/vector-icons";

export const Block = ({ text, iconName, value }) => (
 <MenuOption
   onSelect={() => alert(`You clicked ${value}`)}
   customStyles={{
     optionWrapper: {
       flexDirection: "row",
       alignItems: "center",
       justifyContent: "space-between",
     },
   }}
 >
   <Text>{text}</Text>
   <Entypo name={iconName} size={24} color="black" />
 </MenuOption>
);

export const Mute = ({ text, iconName, value }) => (
 <MenuOption
   onSelect={() => alert(`You clicked ${value}`)}
   customStyles={{
     optionWrapper: {
       flexDirection: "row",
       alignItems: "center",
       justifyContent: "space-between",
     },
   }}
 >
   <Text>{text}</Text>
   <Entypo name={iconName} size={24} color="black" />
 </MenuOption>
);
export const Follow = ({ text, iconName, value }) => (
 <MenuOption
   onSelect={() => alert(`You clicked ${value}`)}
   customStyles={{
     optionWrapper: {
       flexDirection: "row",
       alignItems: "center",
       justifyContent: "space-between",
     },
   }}
 >
   <Text>{text}</Text>
   <SimpleLineIcons name={iconName} size={24} color="black" />
 </MenuOption>
);

export const Why = ({ text, iconName, value }) => (
 <MenuOption
   onSelect={() => alert(`You clicked ${value}`)}
   customStyles={{
     optionWrapper: {
       flexDirection: "row",
       alignItems: "center",
       justifyContent: "space-between",
     },
   }}
 >
   <Text>{text}</Text>
   <EvilIcons name={iconName} size={24} color="black" />
 </MenuOption>
);
export const Question = ({ text, iconName, value }) => (
 <MenuOption
   onSelect={() => alert(`You clicked ${value}`)}
   customStyles={{
     optionWrapper: {
       flexDirection: "row",
       alignItems: "center",
       justifyContent: "space-between",
     },
   }}
 >
   <Text>{text}</Text>
   <SimpleLineIcons name={iconName} size={24} color="black" />
 </MenuOption>
);
export const NotInterested = ({ text, iconName, value }) => (
 <MenuOption
   onSelect={() => alert(`You clicked ${value}`)}
   customStyles={{
     optionWrapper: {
       flexDirection: "row",
       alignItems: "center",
       justifyContent: "space-between",
     },
   }}
 >
   <Text>{text}</Text>
   <Entypo name={iconName} size={24} color="black" />
 </MenuOption>
);

For each component here, we are destructuring three props: text, iconName, and value, coming from each of the components we passed to CustomMenu.js.

Basically, we’re displaying a text and an icon and using onSelect to fire an event when the option is clicked.



In the CustomMenu.js file, we are importing all the components (Block, Mute, Follow, Why, Question, and NotInterested) and using them as the MenuOptions child. Then, we pass the value for the three props we destructured earlier.

We also created a divider component and gave it its style:

import { StyleSheet, Text, View } from "react-native";
import React from "react";
import {
 Menu,
 MenuProvider,
 MenuOptions,
 MenuTrigger,
} from "react-native-popup-menu";
import { Entypo } from "@expo/vector-icons";
import {
 Block,
 Mute,
 Follow,
 Why,
 Question,
 NotInterested,
} from "./CustomContents";

const Divider = () => <View style={styles.divider} />;
const CustomMenu = () => {
 return (
   <MenuProvider style={styles.container}>
     <Menu>
       <MenuTrigger
         customStyles={{
           triggerWrapper: {
             top: -20,
           },
         }}
       >
         <Entypo name="dots-three-horizontal" size={24} color="black" />
       </MenuTrigger>
       <MenuOptions
         customStyles={{
           optionsContainer: {
             borderRadius: 10,
           },
         }}
       >
         <NotInterested
           text="Not interested"
           value="Not Interested"
           iconName="emoji-sad"
         />
         <Divider />
         <Block text="Block" value="Block" iconName="block" />
         <Divider />
         <Mute text="Mute" value="Mute" iconName="sound-mute" />
         <Divider />
         <Follow text="Follow" value="Follow" iconName="user-follow" />
         <Divider />
         <Why text="Why this ad?" value="why this ad" iconName="question" />
         <Divider />
         <Question
           text="Report this ad"
           value="Report this ad"
           iconName="flag"
         />
       </MenuOptions>
     </Menu>
   </MenuProvider>
 );
};

export default CustomMenu;

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#fff",
   marginVertical: 100,
   marginHorizontal: 100,
 },
 divider: {
   height: StyleSheet.hairlineWidth,
   backgroundColor: "#7F8487",
 },
});

Now, we can check what we have:

Menu Icon Options On Click

Pop-up menus with a scroll view

For the rendering of the pop-up menu with a scroll view, we can use either FlatList or ScrollView.

Just like normal, we will create a ScrollViewMenu.js file. We will also create a data.js file where we will be getting the data used for the scroll view. Inside this file, we will do the ScrollView implementation using both FlatList and ScrollView.

The data we will use to create the scroll view will be created in the data.js file:

// data.js
import { Entypo } from "@expo/vector-icons";
export const data = [
 {
   id: 1,
   name: "Mute",
   icon: <Entypo name="sound-mute" size={24} color="black" />,
 },
 {
   id: 2,
   name: "Block",
   icon: <Entypo name="block" size={24} color="black" />,
 },
 {
   id: 3,
   name: "Attachment",
   icon: <Entypo name="attachment" size={24} color="black" />,
 },
 {
   id: 4,
   name: "Battery",
   icon: <Entypo name="battery" size={24} color="black" />,
 },
 {
   id: 5,
   name: "Behance",
   icon: <Entypo name="behance" size={24} color="black" />,
 },
 {
   id: 6,
   name: "Bookmark",
   icon: <Entypo name="bookmark" size={24} color="black" />,
 },
 {
   id: 7,
   name: "Chat",
   icon: <Entypo name="chat" size={24} color="black" />,
 },
 {
   id: 8,
   name: "Camera",
   icon: <Entypo name="camera" size={24} color="black" />,
 },
];

For FlatList, we will import the data from the newly created data.js file and import the menu pop-up APIs. The array of data will be passed to the FlatList API component and will be mapped over so each item can be rendered in the scroll view:

// ScrollViewMenu.js
import { StyleSheet, Text, View, FlatList, ScrollView } from "react-native";
import React from "react";
import {
 Menu,
 MenuProvider,
 MenuOptions,
 MenuOption,
 MenuTrigger,
} from "react-native-popup-menu";
import { Entypo } from "@expo/vector-icons";
import { data } from "../data";

const ScrollViewMenu = () => {
 return (
   <MenuProvider style={styles.container}>
     <Menu>
       <MenuTrigger
         customStyles={{
           triggerWrapper: {
             top: -40,
           },
         }}
       >
         <Entypo name="dots-three-vertical" size={24} color="black" />
       </MenuTrigger>
       <MenuOptions>
         <FlatList
           data={data}
           keyExtractor={(item) => item.id}
           style={{ height: 200 }}
           renderItem={({ item }) => (
             <MenuOption
               onSelect={() => alert(item.name)}
               customStyles={{
                 optionWrapper: {
                   flexDirection: "row",
                   alignItems: "center",
                   justifyContent: "space-between",
                 },
               }}
             >
               <Text>{item.name}</Text>
               <Text>{item.icon}</Text>
             </MenuOption>
           )}
         />
       </MenuOptions>
     </Menu>
   </MenuProvider>
 );
};

export default ScrollViewMenu;

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#fff",
   marginVertical: 200,
   marginHorizontal: 100,
 },
});

Now, we can check what we have in our simulator/emulator:

For ScrollView, the same process will be repeated except we’ll use the ScrollView API component:

import { StyleSheet, Text, View, FlatList, ScrollView } from "react-native";
import React from "react";
import {
 Menu,
 MenuProvider,
 MenuOptions,
 MenuOption,
 MenuTrigger,
} from "react-native-popup-menu";
import { Entypo } from "@expo/vector-icons";
import { data } from "../data";

const ScrollViewMenu = () => {
 return (
   <MenuProvider style={styles.container}>
     <Menu>
       <MenuTrigger
         customStyles={{
           triggerWrapper: {
             top: -40,
           },
         }}
       >
         <Entypo name="dots-three-vertical" size={24} color="black" />
       </MenuTrigger>
       <MenuOptions>
          <ScrollView style={{ height: 200 }}>
           {data.map((item) => (
             <MenuOption
               key={item.key}
               customStyles={{
                 optionWrapper: {
                   flexDirection: "row",
                   alignItems: "center",
                   justifyContent: "space-between",
                 },
               }}
             >
               <Text>{item.name}</Text>
               <Text>{item.icon}</Text>
             </MenuOption>
           ))}
         </ScrollView>
       </MenuOptions>
     </Menu>
   </MenuProvider>
 );
};

export default ScrollViewMenu;

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#fff",
   marginVertical: 200,
   marginHorizontal: 100,
 },
});

Finally, we’ve successfully implemented a pop-up menu in React Native with a scroll view as seen in the simulator/emulator above!

Conclusion

I hope this article has given you the insight to build your own React Native pop-up menu designed for your preference!

They certainly come in handy when building mobile apps in React Native that have several options inside the pop-up menu. Pop-up menus allow the user to scroll to their preferred options as well.

Hopefully, this article also showed you how to implement icons in the pop-up menu, and use things like dividers.

You can check out the full code here.

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — .

Taofiq Aiyelabegan Taofiq is a technical writer and software engineer working on building mobile and web applications. Additionally, he likes to contribute to open source projects and exploring new topics.

Leave a Reply