Main Menu

This guide covers implementation of the main menu.

Data Model

The main menu expects the API to return the following structure:

{
  "menu": {
    "items": [{
      "text": 'Menu Item Text',           // required
      "image": '/path/to/image.png'       // optional - An image to display to the left of the text
      "header": 'html or data'            // optional - HTML or data to display in the header of the menu card for this item
      "footer": 'html or data'            // optional - HTML or data to display in the footer of the menu card for this item

      // For expandable items only
      "expanded": (undefined|true|false), // When `true` or `false`, the item will be rendered as an
                                          // expandable section.
                                          // When `undefined` the menu will slide to the next level of
                                          // the when the item is clicked.

      // For links only
      "href": `/s/[subcategoryId]`,       // optional - the Next.js route pattern
      "as": `/s/${i}`,                    // optional - the URL for the link

      // For parent items only
      "items": []                         // optional - child items to display when the user taps on
                                          // this item.  Child items have the same structure.
    }]
  }
}

Each of your API endpoints should return data for the main menu using the appData callback of fulfillAPIRequest. For example:

import fulfillAPIRequest from 'react-storefront/props/fulfillAPIRequest'

export default async function myApiEndpoint(req, res) {
  res.json(
    await fulfillAPIRequest(req, {
      appData: () => Promise.resolve({
        menu: {
          items: [{
            ...
          }]
        }
      }),
      pageData: ...
    })
  )
}

Customizing the UI

The <Menu/> component can be found in components/Header.js in the starter app.

Side of the screen

To control which side of the screen the menu slides in from, set the anchor prop to right or left.

Adding content to the top or bottom of each card

You can add HTML content to the top or bottom of each menu card by adding a header or footer key to any menu item returned by your API. The value can be HTML that will be rendered verbatim...

// Menu data

{
  "text": "Shirts",
  "header": "<p>Custom header HTML here</p>",
  "items": [...]
}

... or an object, which you can render using custom logic using the Menu component's renderHeader and renderFooter props:

// Menu data

{
  "text": "Shirts",
  "header": {
    { "text": "Men's Shirts", "as": "/s/[subcategoryId]", "href": "/s/mens-shirts" },
    { "text": "Womens's Shirts", "as": "/s/[subcategoryId]", "href": "/s/womens-shirts" },
    { "text": "Childrens's Shirts", "as": "/s/[subcategoryId]", "href": "/s/childrens-shirts" }
  },
  "items": [...]
}
// Menu component

<Menu
  renderHeader={item => (
    <ul>
      {item.header.popular.map((link, i) => {
        <li key={i}>
          <Link as={link.as} href={link.href}>{link.text}<Link>
        </li>
      })}
    <ul>
  )}
/>

Customizing item rendering

You can override the default way each item is rendered using the renderItem prop:

<Menu
  renderItem={item => {
    if (/* some condition */) {
      return <div onClick={...}>{/* custom item */}</div>
    } else {
      return null // render the default item UI
    }
  }}
/>

You can also override just the inner contents of each item, leaving the underlying ListItem and click behavior in place using renderItemContent

<Menu
  renderItemContent={item => {
    if (item.seasonal) {
      // render custom content
      return (
        <div>
          {item.text} <span class={classes.seasonal}>limited time only!</span>
        </div>
      )
    } else {
      // render the default item content
      return null
    }
  }}
/>