Free Astro tutorials for complete beginners.

Beginner's Guide to Astro

How to Add a Navigation Menu in Astro

Build a Header component with links, mark the page you're on, and add a phone menu that opens and closes with no JavaScript. One file, about 60 lines.

Tested on Astro 7.4

Cover image for How to Add a Navigation Menu in Astro
In this guide
  1. Step 1: Make the Header component
  2. Step 2: Understand the three parts
  3. Step 3: Add it to your layout
  4. Step 4: Build and check
  5. Make it better
  6. Where to go next
  7. Frequently asked questions

A menu in Astro is one component. You list your links once. Astro marks the page you’re on. A <details> tag gives phones a menu that opens and closes, with no JavaScript. You put the component in your layout, and every page gets it.

Step 1: Make the Header component

Create src/components/Header.astro. Paste in all of this:

---
const links = [
  { href: '/', label: 'Home' },
  { href: '/about/', label: 'About' },
  { href: '/blog/', label: 'Blog' },
];

const here = Astro.url.pathname.replace(/\/$/, '') || '/';

function isCurrent(href: string) {
  const target = href.replace(/\/$/, '') || '/';
  if (target === '/') return here === '/';
  return here === target || here.startsWith(target + '/');
}
---

<header>
  <a href="/" class="logo">My Site</a>

  <nav class="wide">
    {links.map((link) => (
      <a href={link.href} aria-current={isCurrent(link.href) ? 'page' : undefined}>
        {link.label}
      </a>
    ))}
  </nav>

  <details class="narrow">
    <summary>Menu</summary>
    <nav>
      {links.map((link) => (
        <a href={link.href} aria-current={isCurrent(link.href) ? 'page' : undefined}>
          {link.label}
        </a>
      ))}
    </nav>
  </details>
</header>

<style>
  header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 1rem;
  }
  nav a {
    margin-left: 1rem;
    text-decoration: none;
  }
  nav a[aria-current='page'] {
    font-weight: bold;
    text-decoration: underline;
  }
  .narrow {
    display: none;
  }
  @media (max-width: 600px) {
    .wide {
      display: none;
    }
    .narrow {
      display: block;
    }
    .narrow nav a {
      display: block;
      margin: 0.5rem 0;
    }
  }
</style>

Change the links to match your pages. Change My Site to your site’s name.

Step 2: Understand the three parts

You don’t need to learn all of it. Here’s the short tour.

The list. The links list at the top holds your menu. To add a page, add a line. The menu is built from this list, so you never write a link twice by hand.

The current page. Astro.url.pathname is the address of the page being built, like /about/. The isCurrent check compares it to each link. When they match, the link gets aria-current="page". Screen readers say “current page” out loud. The CSS makes it bold and underlined.

The check also counts pages inside a section. So on /blog/my-first-post/, the Blog link lights up too. The Home link only lights up on the home page.

The phone menu. The <details> tag is plain HTML. It shows the word in <summary>. Tap it and the rest opens. Tap again and it closes. The CSS shows the normal menu on wide screens. On screens under 600 pixels wide it shows the <details> menu instead.

Step 3: Add it to your layout

Open your layout, like src/layouts/Layout.astro. Import the header at the top, then put it as the first thing in <body>:

---
import Header from '../components/Header.astro';
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>My Site</title>
  </head>
  <body>
    <Header />
    <slot />
  </body>
</html>

Keep anything else your layout already has in <head>. Just add the import and the <Header /> line.

Step 4: Build and check

Run npm run build. The end of the output lists your pages. Here is ours:

Terminal
$ npm run build
19:31:29   ├─ /about/index.html (+4ms)
19:31:29   ├─ /blog/first-post/index.html (+2ms)
19:31:29   ├─ /blog/index.html (+1ms)
19:31:29   ├─ /index.html (+1ms)
19:31:29 [build] Complete!

Then run npm run dev and click around. The link for the page you’re on is bold. Make your browser window narrow. The links turn into a “Menu” button. Tap it to open the list.

We ran this on a fresh Astro 7.4 project with four pages. Each page marked the right link, and /blog/first-post/ marked Blog. We also opened it at phone size. The menu opened and showed the current page in bold.

Building with Claude? Paste this guide into Claude Code and ask it to do it for you.

Make it better

  • Close the menu after a tap. On a normal site, each link loads a new page, so the menu starts closed again. No extra code needed.
  • Add a “skip to content” link. It helps people who use a keyboard jump past the menu.
  • Style it your way. All the CSS is in the <style> part. It only affects this component, so it won’t break the rest of your site.

Where to go next

Frequently asked questions

How do I highlight the current page in an Astro menu?

Compare each link to Astro.url.pathname, which is the address of the page being built. When they match, add aria-current="page" to that link and style it with CSS.

Do I need JavaScript for a phone menu?

No. The HTML details element opens and closes on a tap by itself. This guide uses it, so the menu works even if JavaScript is off.

How do I add a new page to the menu?

Add one line to the links list at the top of Header.astro. Both the wide menu and the phone menu use that list, so they stay the same.

Stuck on a step?

Check the common errors guide, look up a word in the glossary, or ask the friendly people in the official Astro and EmDash Discords. How to ask so people can help.

Keep reading

  1. How to Register a Domain Name and Connect It to Your Astro Site

    Buy a domain for about $11 a year, then point it at Cloudflare or Netlify. We did this for astronursery.com, and these are the exact steps.

    Tutorials, 8 minute read, November 8, 2026

  2. How to Add Google Analytics to an Astro Site (and a Simpler Option)

    Paste Google's tag into your Astro layout with one small change, is:inline, and it works on every page. Or use Cloudflare Web Analytics, which needs no cookie banner.

    Tutorials, 6 minute read, November 8, 2026

  3. How to Add a Contact Form to an Astro Site (No Server Needed)

    A static site can't receive form submissions on its own. A free form service can. Here's a working contact form in ten lines, with spam protection built in.

    Tutorials, 7 minute read, November 8, 2026

Try "blog", "deploy" or "EmDash".