---
title: Shopify Menus
description: Replace the hardcoded nav and footer menus with Shopify-powered menus.
type: guide
---

# Shopify Menus



## How to use

```bash
/vercel-shop:enable-shopify-menus
```

<div className="pb-6" />

{/* BEGIN SKILL CONTENT: enable-shopify-menus */}

# Enable Shopify Menus

By default, the storefront's nav and footer render hardcoded fallback items: an inline `const items: MenuItem[] = [...]` in `Nav` (`components/nav/index.tsx`) and an empty `const items: MenuItem[] = []` in `Footer` (`components/footer/index.tsx`). The components themselves (`QuickLinks`, `MobileMenu`, `Footer`'s `FooterMenu`) already consume `MenuItem[]` in Shopify shape and render up to three levels of nesting. This skill swaps that inline default to a live Shopify menu fetched by handle, keeping the inline items as the fallback if the menu is missing or empty.

## Before you start

Ask the user two questions in order:

### 1. Which menus do you want to fetch from Shopify?

* **Nav menu** — replaces the hardcoded items used by the desktop quick links and mobile sheet.
* **Footer menu** — replaces the hardcoded footer columns.
* **Both**

### 2. What are the Shopify menu handles?

Ask for each selected menu. Defaults: `main-menu` for nav, `footer` for footer.

Wait for the user to answer before proceeding.

***

## Part A: Enable Shopify nav menu

Skip this section if the user did not select the nav menu.

Edit `components/nav/index.tsx`. Add the `getMenu` import and swap the data source:

```tsx
import { getMenu } from "@/lib/shopify/operations/menu";
```

Inside `Nav`, keep the existing inline default and swap the data source so the Shopify menu takes precedence:

```tsx
const defaultItems: MenuItem[] = [
  { id: "default-nav-shop", title: "Shop", url: "/collections/all", type: "HTTP", items: [] },
];
const menu = await getMenu({ handle: "NAV_HANDLE" });
const items = menu?.items ?? defaultItems;
```

Replace `"NAV_HANDLE"` with the handle the user provided. `QuickLinks` and `MobileMenu` already accept `MenuItem[]`, so no other component changes are needed. Keep the inline default as the fallback.

***

## Part B: Enable Shopify footer menu

Skip this section if the user did not select the footer menu.

Edit `components/footer/index.tsx`. Make the component async, add the `getMenu` import, and swap the data source:

```tsx
import { getMenu } from "@/lib/shopify/operations/menu";
```

Change the signature to `async` and replace `const items: MenuItem[] = [];` with:

```tsx
const menu = await getMenu({ handle: "FOOTER_HANDLE" });
const items = menu?.items ?? [];
```

Replace `"FOOTER_HANDLE"` with the handle the user provided. `FooterMenu` already accepts `MenuItem[]`. The footer's default is empty (no columns), so falling back to `[]` simply hides the section when the Shopify menu is missing or empty.

The `Footer` callsite in `app/layout.tsx` (or wherever it is rendered) already passes `locale`; if it is not wrapped in `<Suspense>`, the menu fetch will block layout render — that is acceptable since `getMenu` is cached with `"use cache: remote"` and `cacheLife("max")`. Wrap in `<Suspense>` only if you specifically want the rest of the page to stream ahead of the footer.

***

## Guardrails

* The `getMenu()` operation in `lib/shopify/operations/menu.ts` already handles caching (`"use cache: remote"`, `cacheTag("menus")`) and URL transformation. Do not duplicate that logic.
* Always preserve the inline fallback so a missing or empty Shopify menu doesn't leave the user with a blank nav or footer.
* External links (URLs starting with `http`) are handled by the existing `MenuLink` helpers in each component — no change needed.

{/* END SKILL CONTENT: enable-shopify-menus */}


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)