Skip to content

A navigation menu in a global ​

What you'll build

One menu for the whole site that editors change in the admin, with labels in every language. Uses: globals, arrays, localization.

1. The global ​

ts
globals: [
  {
    slug: 'navigation',
    label: { en: 'Navigation', th: 'เมนู' },
    icon: 'link',
    access: { read: () => true },
    fields: [
      {
        name: 'links',
        type: 'array',
        maxRows: 8,
        fields: [
          { name: 'label', type: 'text', required: true, localized: true },
          { name: 'url', type: 'text', required: true },
          { name: 'external', type: 'boolean' },
        ],
      },
    ],
  },
],

Editors find it under Settings → Navigation, add links and drag them into order.

2. Show it in the layout ​

ts
export default defineEventHandler(async (event) => {
  const cms = await useEasyCMS()
  return cms.findGlobal('navigation', { locale: getQuery(event).locale as string | undefined })
})
tsx
import { getEasyCMS } from '@easy-cms/next'
import type { ReactNode } from 'react'
import config from '@/easy-cms.config'

export default async function Layout({ children }: { children: ReactNode }) {
  const nav = await (await getEasyCMS(config)).findGlobal('navigation', { locale: 'th' })
  return (
    <html lang="th">
      <body>
        <nav>
          {nav.links?.map((link) => (
            <a key={link.id} href={link.url} {...(link.external ? { target: '_blank', rel: 'noopener' } : {})}>
              {link.label}
            </a>
          ))}
        </nav>
        {children}
      </body>
    </html>
  )
}

In Nuxt, fetch it once in app.vue with useFetch('/api/navigation', { query: { locale } }).

Tips ​

  • A link to a page that may be renamed? Use a relationship to pages instead of url, and build the path from the related page's slug.
  • With drafts: true on the global, editors can prepare a new menu and publish it at once.

Released under the MIT License.