When navigating an app on the web or mobile you may commonly come across custom menus with app-specific options that pop when right-clicking or tapping certain elements of the application. We call these context menus, and they are a type of user interface that can help to provide a guided experience to users to take specific actions depending on where that user action has taken place.
For our project, we will create a new React project—I prefer to use NextJS, but standard 'create-react-app' works just fine as well.
We are also using styled-components for styling in this project which you can install with:
Note that if you are not using the latest version of NextJS and are having issues with fast refresh and your styling you might have to add a .babelrc file with the following:
We can then go in and remove all of the default content and styling and leave ourselves a classic “hello world" home page.
We’ll want to create a few extra directories inside of /src to keep our code organized:
To showcase an example of where a context menu might be implemented, we will create a simple list of items that could mimic any number of elements in an application such as posts, orders, or anything else that might have a list of items where custom actions could be helpful.
First, we will create a List component in our /components directory. I prefer to keep all of my component related files together by creating directories for each of my components that contain the following:
In a more involved project, I tend to store other component related files here as well such as StorybookJS stories, tests, etc.
First, we will create the data that we want to use for our list in List.data.ts:
Then we will create our component itself, import that data, and map over it to create a list of items to work with:
You will notice that we have also imported a styled Container element from List.styles.ts which looks like this:
If everything is correct we should have a simple list component to work with that has a bit of hover feedback.
Now that we have a list component in place, our first task is to disable the default right-click behavior that would typically pull up the system right-click menu so that we can replace it with our own context menu.
To accomplish this, we will use the onContextMenu prop and prevent the default behavior like so:
You will notice when right-clicking our list you no longer see the system menu pop up. However, because we only prevented that default behavior for the containing div of our list component you can still right-click elsewhere in the app and pull up the system menu.
Now we can create a new component called ContextMenu which will serve as our custom context menu that we provide for the user when they interact with our list.
First, we will create a ContextMenu.data.ts file to store the menu options:
Then we will import that into a new ContextMenu.component.tsx file and map over it to create our list of options:
Then we will add styles to our MenuContext.styles.ts to give ourself something a little more visual:
If we add the component to our home page it should look something like this:
Now that we have a component to work with, we need to implement a custom hook to handle the logic and state for our context menu so that it can appear and disappear at the right location based on user interaction.
The first thing that we will do is implement a custom useContextMenu hook in our /hooks directory.
Here we will be using state to track whether or not the user has right-clicked (as well as if they have clicked elsewhere after right-clicking) and the coordinates of the user’s right-click interaction. We will also register an event listener in a useEffect to listen for that user’s clicks.
This gives us all of the state and logic that we need to handle our custom context menu. Notice how we are sure to add a clean-up function to remove the click event listener at the end of the useEffect to prevent potential memory leaks.
Now that our custom hook is in place we can return to our List component. We first import and destructure the elements of our custom hook to be used as controls for our context menu. Then we once again target the onContextMenu prop to ensure we set the clicked state to true, and save the x and y coordinates of that click to be passed into our context menu as props.
Finally, we conditionally render the context menu based on whether or not our clicked state is true.
To finish off we need to go to our ContextMenu component where we are receiving our coordinate props and pass those props to the Container styled component that we created so that we can use them to position our menu based on the user’s click coordinates.
In the styles file we receive the coordinates as props and pass them as string interpolations so that the menu dynamically displays based on the user’s click coordinates.
If everything is done correctly our new menu should look something like this when right-clicking anywhere within the List component:
In this article we’ve covered all of the basics on how to set up and organize our project, create menu components, disable default browser right-click behavior, and utilize a custom hook to manage the state and logic. With this knowledge, you can create powerful custom context menu features and guided user interface experiences for your applications. Keep in mind that we did not cover mobile and dealing with touch-based interfaces, and so that is another consideration to weigh before implementing this on your next project.