 

Drupal・6 min read・Last update on July 12, 2023

# Next.js for Decoupled Menus Initiative

 

 

 

 ![Next.js for Decoupled Menus Initiative](/sites/default/files/remediation-media/64e79ba21619090d9ea42d4d_Next.js_for_Decoupled_Menu_Initiative.avif) 

 



 

   Table of contents  - [Why do we Decouple our Menus?](#why-do-we-decouple-our-menus)
- [Our Goal](#our-goal)
- [A glimpse of the Drupal menu API (data)](#a-glimpse-of-the-drupal-menu-api-data)
- [What does our NextJS code do – Static Generation](#what-does-our-nextjs-code-do-static-generation)
- [Conclusion &amp; Future Work](#conclusion-future-work)
 
  

 

Decoupled or Headless CMS fully or progressively decouples the frontend from the backend. In this approach, Drupal serves as a backend content vault, while one can choose a frontend technology that suits them best for a project. The Decoupled Menus initiative is a step towards exploring potential of Decoupled Drupal integration with various frameworks by making Drupal Menus as an API to be integrated with a modern JS framework such as React/Next.js. This idea has been officially launched as Drupal Core Strategic Initiative. You can find more information on this initiative [here](/insights/driesnote-drupalcon-north-america-2021).

  ![Decoupled Menus Initiative - NextJS](/sites/default/files/c3-body-media/cbf63e1d7d2fb02be419eb9d.avif) *Source - Drupal.org*

We, at QED42, have chosen NextJS - a Server Side Rendering and Static Generation framework of React as the frontend to pre-render the menu. Due to its features like prefetching, fast refresh, automatic routing, compilation and bundling, performance optimization, SEO, and much more.

### Why do we Decouple our Menus?

**Background:** Every time a non-developer wants to change a menu item on the Drupal side, a developer has to get involved to implement it on the JS side. That developer must change and rebuild the code, and then deploy those changes to production. These steps may take days to complete depending on the availability of the developer, and the complexity of the deployment process.

**The solution suggested by the Drupal team:** By providing menu items via Drupal’s JSON:API, these deployment steps can be eliminated and a core Javascript package can reduce the lines of code that a JS developer has to write in the first place. Have a look at the **Decoupled menus in Drupal Demo** by **Gabe Sullice** for more details about the initiative, discussing the current user’s authentication status, menu hierarchies and dropdown, user-defined menu items and routing.

#### Other reasons are as follows –

1. Recognize improvements for Headless CMS, with the menu as a use case
2. Not all Drupal modules need to be modules
3. Especially while updating the version of Drupal, most of these do not require the database and other features of Drupal
4. Some Drupal modules do not make complete utilization of Drupal and can be built in other languages
5. Shorter bandwidth to rebuild all modules in a reasonable timeframe.
6. NextJS came into view because its modules are much simpler and faster to write.
7. We can create features that are missing and much needed to develop.
8. We do not want to create functionality that already exists.
9. Make Decoupled Drupal and menus attractive to non-Drupal FE developers.

**Please Note:** You can join the Decoupled-menu-Initiative slack channel [here](https://drupal.slack.com/archives/C016N3HTHRD). The weekly meetings take place every Tuesday at 14:00 UTC and again at 4:00 UTC at the Slack channel itself.

### Our Goal

In support of this initiative, we will present you with a NextJS menu component that recursively renders the menu data provided by the Drupal API and displays it in the form of a dropdown. This can be considered Part-1 of our “**NextJS for Decoupled menus**” series. In this blog we will share the setup, components, prefetching and rendering of the menu. In the next part, we will be integrating this with Drupal.

‍

Deployed on: <https://nextjs-decoupled-menus.vercel.app/>

### A glimpse of the Drupal menu API (data)

The menu APIs provided by Drupal have JSON data stored in a linear structure rather than a hierarchical structure. Therefore, a frontend developer has to construct a tree-like hierarchy from the data to render the menu in a drop-down format. That is what we have focused on currently and have explained in the next section. Given below are the APIs, with the Drupal-menu-hierarchy key for each sub-menu, revealing its hierarchy level.

[https://decoupled-menus.jsonapi.dev/system/menu/Drupal-wiki/linkset](https://decoupled-menus.jsonapi.dev/system/menu/drupal-wiki/linkset)

<https://decoupled-menus.jsonapi.dev/system/menu/main/linkset>

### What does our NextJS code do – Static Generation

  ![Decoupled Menus Initiative - NextJS](/sites/default/files/c3-body-media/44f744905fa9bd7dfba0764a.avif) *Architectural Diagram of Current Application Structure*

**The following steps describe the functionality of our existing application –**

**Step 1:** The**\_app.js** is the root file of our application. The Component returned by it is wrapped in our custom Layout component, with page properties passed as children. This file also utilizes the Head Component of NextJS as the header of a page and includes the title and all the meta-tags that are one of the reasons for our 100% SEO.

 ```

function MyApp({ Component, pageProps }) {
  return <>
    <Head>
      <title>Decoupled Menus</title>
      <meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
      <meta name="description" content="Demo of decoupled menus in nextjs with great SEO" />
      <meta name="viewport" content="width=device-width, initial-scale=1"></meta>
    </Head>
  </>
<Layout>
      <Component {...pageProps} />
  </Layout>
}

export default MyApp
```

[https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/pages/\_app.js](https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/pages/_app.js)

  ![Decoupled Menus Initiative - NextJS](/sites/default/files/c3-body-media/615e4b39c392147a2120d1db.avif) *Get better performance, accessibility, SEO and much more with NextJS*

**Step 2:**The**index.js** file is the Home page of our application. It performs like a logical component because it fetches data from the provided Drupal API using **fetch()**in **getStaticProps()**and returns the JSON data as props to **\_app.js**.

 ```

const menuId = "drupal-wiki"
export async function getStaticProps() {
  const result = await (await fetch(`https://decoupled-menus.jsonapi.dev/system/menu/${menuId}/linkset`)).json()
  return {
    props: { data: result.linkset[0].item }
  }
}
export default function Home() {
  return 
}

```

<https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/pages/index.js>

  
**Step 3:**The components folder, which lies outside the pages folder contains our presentational components - menuBar, Layout and Footer.

  ![Decoupled Menus Initiative - NextJS](/sites/default/files/c3-body-media/aca1f7232cd257db265fc9f5.avif)   
**Step 4:**The Layout component further passes the props-children to the menuBar, which was passed by the **\_app.js** component. It also calls the Footer component in the end.

 ```

const Layout = ({ children }) => {
    return <div className="content">
<header>
       <h1>Decoupled Menus Demo using NextJS</h1>
   	</header>
<div className="menuBar">
        <MenuBar data={children.props.data} />
	</div>
        <Footer />
    </div>
}
export default Layout;
```

<https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/components/Layout.js>

  
**Step 5:**The menuBar component makes use of the Drupal-API data passed as props to render it as a menu of our page. we have used the **menuHelper** utility to create a menu Tree and return it to the menuBar.

- Mapping through the menu data array, we have used the given menu-hierarchy of each menu object to add submenus to the mainRoot as children.
- We further iterate through each sub-menu to add further children until the leaf node (last menu item) is reached based on the given hierarchy.
- This creates our menu Tree in the mainRoot object using Loops.

 ```

const util = ((data) => {
    let mainRoot = { children: [] } 
  if (data !== undefined)
   data.map(menuObj => {
 
let hierarchyArr = menuObj['drupal-menu-hierarchy'][0].split(".")
let curr = mainRoot;

 for (let hIdx = 1; hIdx < hierarchyArr.length; hIdx++) {
 let menuId = parseInt(hierarchyArr[hIdx])
        if (curr.children[menuId] === undefined) {
         curr.children[menuId] = { ...menuObj, children: [] } 
         } else {
           curr = curr.children[menuId]
                }
            }
        })
return mainRoot
})
export default util;
```

<https://github.com/VertikaJain/nextjs-decoupled-menus/blob/main/utilities/menuHelper.js>

- Then, we have rendered the main level of the menu Tree using the [TreeView API of Material UI](https://material-ui.com/api/tree-view/).

 ```

const menuBar = ({ data }) => {
return <div>
        {/* Main root level - using single map */}
        <TreeView
            className="treeView"
            defaultCollapseIcon={<ExpandMoreIcon />}
            defaultExpandIcon={<ChevronRightIcon />}
        >
            {mainRoot.children.map(child => {
                return createTreeItem(child)
            })}
        </TreeView>
    </div>
}

```

- Lastly, the inner root levels are rendered Recursively using the [TreeItem API of Material UI](https://material-ui.com/api/tree-item/) in the user-defined method create**TreeItem()**. This method checks if the menu item is the last item in its particular hierarchy. If yes, then it simply displays the content/link, else it recurses further till the last **menu-item** is reached.

 ```

// Inner roots level - using recursion
const createTreeItem = (menuObj) => {
 // Links for Leaf nodes
 if (menuObj.children.length === 0 || menuObj.children === []) {
  return <TreeItem
   className="treeItem"
   key={menuObj['Drupal-menu-hierarchy'][0]}
   nodeId={menuObj['Drupal-menu-hierarchy'][0]}
   label={<a href={menuObj.href}>- {menuObj.title}</a>}>
       </TreeItem>
    }
    // Recursing through sub-menus
 else
  return <TreeItem
  key={menuObj['Drupal-menu-hierarchy'][0]}
  nodeId={menuObj['Drupal-menu-hierarchy'][0]}
  label={menuObj.title}>
  {menuObj.children.map(child => createTreeItem(child))}
        </TreeItem>
}
export default menuBar;
```

[https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/components/menuBar.js](https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/components/MenuBar.js)

**Step 6:**The **Footer** component simply displays the Copyright content.

<https://github.com/vertikajain/nextjs-decoupled-menus/blob/main/components/Footer.js>

  
**Step 7:**The **\_document.js** file enables the material UI icons to be available at the first render, avoiding unusual FOUC.

[https://github.com/VertikaJain/nextjs-decoupled-menus/blob/main/pages/\_document.js](https://github.com/VertikaJain/nextjs-decoupled-menus/blob/main/pages/_document.js)

### Conclusion &amp; Future Work

The purpose of this blog was to showcase how one can use the Drupal Menu API with NextJS as the frontend for pre-rendering and how it improves SEO ranking. At the moment this application is only deployed to Vercel, and has not yet been converted into a package. Stay tuned for our upcoming blog that will revolve around integrating our NextJS package with Drupal. Interested to know more about the other Drupal Initiatives? Click [here](/insights/driesnote-drupalcon-north-america-2021) to read more.

 



Written by

Vertika Jain

JavaScript Developer

 

 

 

 

 

 

 ## We'd love to talk about your business objectives

 [ Contact Us  ](/contact)