A Multi-level React Dropdown Menu Example

by Vincy. Last modified on February 11th, 2024.

The React dropdown menu component is easy to create. Learning React to create UI components on your own is a very good exercise.

The dropdown menu is about showing a list of clickable menu links or selectable options on a webpage. Some of the utilities of a React dropdown menu component are listed as follows.

  1. Creating a header dropdown menu to list the navigation links.
  2. Site settings to choose a language menu among the multilingual options.
  3. Show menu group with category-subcategory classification.

View Demo

There are various ways to render a dropdown menu using React. It has a built-in component <Select> to display a dropdown list. It can also import Bootstrap libraries to use <Dropdown> components to display selectable options to the UI.

react dropdown menu

About this example

In this tutorial, we create a custom React component to show a dropdown. It displays a multi-level dropdown menu on the site header.

The menu options are dynamic, and they are from the database. The database can map the main and subcategory category relationships.

The steps to create a custom React dropdown menu are as follows.

  1. Create a database and import the menu data into it.
  2. Fetching the menu records for generating a data source object on the client side.
  3. Build a React component to show the main menu container.
  4. Iterate the submenu dropdown based on the hovered main menu option.

Create a database and import the menu data.

This SQL has the menu table structure and data. Create a database and import this script to see the menu items on the UI when seeing the React component.

sql/dropdown.sql

-- Table structure for table `tbl_menu`
--

CREATE TABLE `tbl_menu` (
  `id` int(11) NOT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

--
-- Dumping data for table `tbl_menu`
--

INSERT INTO `tbl_menu` (`id`, `parent_id`, `name`) VALUES
(1, NULL, 'PHP Contact Form'),
(2, NULL, 'PHP Shopping Cart'),
(3, 1, ' Contact form in wordpress'),
(4, 1, 'Responsive Contact Form'),
(5, 2, 'JavaScript Shopping Cart'),
(6, 2, 'Bootstrap for Shopping Cart'),
(7, NULL, 'Payment Integration'),
(8, NULL, 'Authentication'),
(9, NULL, 'Tutorial'),
(10, 1, 'Contact Form in WordPress'),
(11, 1, 'Bootstrap Contact Form'),
(12, 2, 'Responsive Shopping Cart'),
(13, 2, 'Shopping Cart like Amazon');

Data fetching using Axios from useEffect() hook

This code uses Axios to proceed with the API fetch over a remote call.

Run the following npm command to use the Axios in your JavaScript server call.

npm install axios

It prepares the HTTP GET request to call the API endpoint. The server will return a data source object array with menu options. Then, this script filters the parent and child menu options.

The fetchDataFromAPI() function is called in the useEffect() callback. This callback prepares the data source for the dropdown component when mounted.

When creating a React Select cascade, we use componentDidMount() to call AJAX on the component mount event.

src/Dropdown.js (Part of the code to perform Axios fetch)

const fetchDataFromAPI = async () => {
    try {
      const response = await axios.get('http://example.com/api/get-menu-options.php');
      const data = response.data;
      setParentItems(data.filter(item => item.parent_id === null));
      setChildItems(data.filter(item => item.parent_id !== null));
    } catch (error) {
      console.error('Error fetching data: ', error);
    }
  };

  useEffect(() => {
    fetchDataFromAPI();
  }, []);

PHP API endpoint for React dropdown data fetch

This PHP file is created as the API endpoint for the React dropdown menu script.

It connects the tbl_menu database and fetches data to form the menu hierarchy.

It evaluates the request method before executing the database operations.

api/get-menu-options.php

<?php
header("Access-Control-Allow-Origin: *");

// Establish MySQL database connection
$servername = "localhost"; // Replace with your MySQL server name
$username = "root"; // Replace with your MySQL username
$password = ""; // Replace with your MySQL password
$dbname = "dropdown"; // Replace with your database name

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// API endpoint to fetch dropdown data
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    // Fetch data from the database using prepared statement
    $query = "SELECT * FROM tbl_menu";
    $stmt = $conn->prepare($query);
    $stmt->execute();
    $result = $stmt->get_result();

    $dropdownData = array();
    while ($row = $result->fetch_assoc()) {
        $dropdownData[] = $row;
    }

    // Prepare JSON response
    echo json_encode($dropdownData);
}
?>

React dropdown component JSX for the parent and child level

This script filters the parent menu from the data source. Then, it maps the parent array to filter out the child menu by the parent index. This filtering helps to map data for the multi-level React dropdown menu.

The filtered menu list is displayed by hovering over the corresponding parent menu item.

src/dropdown.js

const filteredChildItems = childItems.filter(item => item.parent_id === activeParentId);
const hasChildItems = filteredChildItems.length > 0;
return (
    <div id='demo-content' className='phppot-container'>
    <div className='textline'>
      <div className='hoverlink'
        onMouseEnter={handleLinkHover}
        onMouseLeave={() => setShowParentContainer(false)}
      >
        <a className='menu' >
          {linkText}
        </a>
      
        {showParentContainer && (
         <div className='parent-container'>
         {parentItems.map(parentItem => {
           const hasChildren = childItems.some(childItem => childItem.parent_id === parentItem.id);
       
           return (
             <div
               key={parentItem.id}
               onMouseEnter={() => handleParentItemHover(parentItem.id)}
               className={parentItem.id === activeParentId ? 'parent-item active' : 'parent-item'}
             >
               <p className={hasChildren ? 'has-children' : ''}>
                 {parentItem.name}
                 {hasChildren && <span className='triangle-arrow'>▸</span>}
               </p>
             </div>
           );
         })}
       </div>
       
       
       
        )}
        {showParentContainer && activeParentId !== null && hasChildItems && (
          <div className='child-container'
            onMouseLeave={handleChildItemHover}
          >
            <div>
              {filteredChildItems.map(childItem => (
                <div key={childItem.id}>
                  <p>{childItem.name}</p>
                </div>
              ))}
            </div>
          </div>
        )}
      </div>
      <a className='menu' href='#'>Products</a>
          <a className='menu' href='#'>Other Offers</a>
          <a className='menu' href='#'>Contact</a>
    </div>
    </div>
  );
};

export default Dropdown;

React dropdown menu event handler on hover

This code defines two state variables. Those are to set the active parent menu and the state of showing the parent dropdown menu.

Initially, all the parent dropdown menu elements will be set as false. On hovering the header link, the handleLinkHover() listener sets the parent menu display state to true. At the same time, the currently active parent id will be updated on the useState.

On hovering the parent menu item, the handleParentItemHover function is called. It shows the second level of categories in the level 2 dropdown menu.

src/dropdown.js

const handleLinkHover = () => {
    setShowParentContainer(true);
    setActiveParentId(null);
 };

 const handleParentItemHover = (parentId) => {
    setShowParentContainer(true);
    setActiveParentId(parentId);
 };

 const handleChildItemHover = () => {
    setShowParentContainer(false);
 };

View Demo Download

Leave a Reply

Your email address will not be published. Required fields are marked *

↑ Back to Top

Share this page