Navigation and Menus

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages.

In a block theme a menu is not a list managed on Appearance > Menus: it is a Navigation block placed in a template part, whose items are blocks stored in a wp_navigation post. This page explains that model from the theme developer’s side: how the block references its menu, which blocks a menu is made of, what happens when no menu is selected, how classic menus are imported, how the mobile overlay is replaced by a template part (7.0), how the current menu item is styled from theme.json (7.1), how the classic register_nav_menus()/wp_nav_menu() model compares, and what makes a menu accessible.

This page was written against WordPress 7.1.3 (released 2026-10-06; 7.1.4, a security release of 2026-10-28, is the version to run in production), PHP 7.4 minimum and 8.3+ recommended, theme.json version 3 and WP-CLI 2.12.0, and checked on 2026-10-28. The Gutenberg plugin (24.2.0) previews later Navigation block changes; nothing here requires it. WordPress 7.2 is in beta and due in December 2026. Every command and its output below was run on a 7.1.3 site with the lumen block theme active; the stack is the one described in Local Development Environments.

The site owner’s view of the same screens (Appearance > Editor > Navigation, renaming and deleting menus) is in Site Editor and Global Styles; template parts in general are covered in Templates and Template Parts.

Two menu systems

WordPress has two independent menu systems, and both are still in core:

Classic menus (since 3.0) Block menus (since 5.9)

Storage

A nav_menu taxonomy term per menu; each item is a nav_menu_item post with _menu_item_* post meta

One wp_navigation post per menu; the items are block markup in post_content

Placement

The theme registers locations with register_nav_menus(); the site owner assigns a menu to a location; the template prints it with wp_nav_menu()

A Navigation block in a template or template part selects a menu by ID with its ref attribute

Admin screen

Appearance > Menus, Customizer > Menus

Appearance > Editor > Navigation (6.3), and the block itself

Item types

Pages, posts, terms, custom links

Any allowed block: links, submenus, Page List, Search, Social Icons, Site Logo, Buttons, and more

REST API

/wp/v2/menus, /wp/v2/menu-items, /wp/v2/menu-locations (5.9), authenticated only

/wp/v2/navigation (5.9); published menus are readable without authentication

WP-CLI

wp menu, wp menu item, wp menu location

wp post with --post_type=wp_navigation

Appearance > Menus appears only when the active theme registers at least one menu location or uses widgets, so it is absent with a pure block theme such as lumen. Classic menus created earlier remain in the database; they are no longer printed anywhere, but the Navigation block can import them.

The Navigation block and wp_navigation posts

The wp_navigation post type is not public, has rest_base navigation, and maps all of its capabilities to edit_theme_options, so only Administrators can create or edit menus. A Navigation block in a template part points at one of these posts:

<!-- wp:navigation {"ref":15} /-->

When the block renders, it loads post 15 and renders its blocks. Only a published wp_navigation post is used; editing the menu in the editor changes that post, not the template part, so the same menu can appear in the header, the footer and the overlay and stays in sync.

Creating the Lumen Press main menu

The Lumen Press header shows Books, News and an About submenu. The menu content is ordinary block markup: Navigation Link blocks (core/navigation-link) for single items and a Submenu block (core/navigation-submenu) that contains further links:

<!-- wp:navigation-link {"label":"Books","type":"custom","url":"/books/","kind":"custom"} /-->
<!-- wp:navigation-link {"label":"News","type":"page","id":7,"url":"/news/","kind":"post-type"} /-->
<!-- wp:navigation-submenu {"label":"About","type":"page","id":4,"url":"/about/","kind":"post-type"} -->
<!-- wp:navigation-link {"label":"About Lumen Press","type":"page","id":4,"url":"/about/","kind":"post-type"} /-->
<!-- wp:navigation-link {"label":"Submissions","type":"page","id":6,"url":"/submissions/","kind":"post-type"} /-->
<!-- wp:navigation-link {"label":"Contact","type":"page","id":5,"url":"/contact/","kind":"post-type"} /-->
<!-- /wp:navigation-submenu -->

The attributes of a link are label, url, kind (post-type, taxonomy or custom), type (page, post, category, a custom post type, or custom), id (the linked object), opensInNewTab, rel, title and description. The url is stored literally and printed as it is: on 7.1.3, changing the News page’s slug did not change the rendered href, so menu links must be updated after a slug change (the editor’s link control shows the linked page and makes this quick). The id of a post-type or taxonomy link is what marks the item as current when that object is viewed. The /books/ link goes to the lp_book archive registered by the lumen-catalog plugin with the books rewrite slug; for archive links the current item is detected by comparing the URL.

With WP-CLI, the content is saved as a wp_navigation post; --porcelain prints only the new ID:

wp post create main-menu.html --post_type=wp_navigation --post_status=publish \
  --post_title="Main menu" --post_name=main-menu --porcelain
# 15

The REST API gives the same view the Site Editor uses. A published menu is readable anonymously, which is harmless because its links are on every page anyway; writes require edit_theme_options:

curl -s https://lumenpress.test/wp-json/wp/v2/navigation/15 | jq '{id, slug, status, title: .title.rendered}'
# { "id": 15, "slug": "main-menu", "status": "publish", "title": "Main menu" }

The header part

The lumen header part places the Navigation block next to the site title and selects the menu, the overlay and the submenu behaviour:

<!-- wp:group {"tagName":"header","align":"full","layout":{"type":"constrained"}} -->
<header class="wp-block-group alignfull">
	<!-- wp:group {"align":"wide","layout":{"type":"flex","justifyContent":"space-between"}} -->
	<div class="wp-block-group alignwide">
		<!-- wp:site-title {"level":0} /-->
		<!-- wp:navigation {"ref":15,"overlayMenu":"mobile","overlay":"mobile-menu","submenuVisibility":"click","ariaLabel":"Main","layout":{"type":"flex","justifyContent":"right"}} /-->
	</div>
	<!-- /wp:group -->
</header>
<!-- /wp:group -->

The Navigation block attributes that matter for a theme:

Attribute Meaning

ref

ID of the wp_navigation post. Without it, the block renders its own inner blocks or, if it has none, the fallback described below.

overlayMenu

mobile (default: collapse behind a menu button below 600px), always, or never.

overlay

Slug of a template part in the navigation-overlay area (7.0); replaces the default overlay.

submenuVisibility

hover (default), click or always (7.0). Replaces the boolean openSubmenusOnClick, which older content still carries and which still wins when present (true means click).

showSubmenuIcon

Show the arrow next to items with a submenu.

hasIcon, icon

Show an icon (handle or menu) on the open button instead of the word "Menu".

maxNestingLevel

Submenu depth allowed in the editor (default 5).

ariaLabel

The aria-label of the <nav> element (block support). If empty, the menu’s title is used.

templateLock

Lock the inner blocks (all, insert, contentOnly).

The allowed children are listed in the block’s allowedBlocks: Navigation Link, Submenu, Page List, Search, Social Links, Spacer, Home Link, Icon, Site Title, Site Logo, Login/out and Buttons.

IDs are site-specific

A ref is a database ID, so a theme that ships {"ref":15} works only on the site where post 15 is the right menu. Exporting the site design from the Site Editor keeps the ref values as they are. On a different site the ID can point at nothing, at a draft or at an unrelated post, and the result differs:

ref points at What visitors see

A published wp_navigation post

That menu

A draft or trashed menu

The fallback: the most recently published menu, or a page list

No post at all

An empty <nav> element, with no warning

For a theme that is distributed, the usual choices are to leave the Navigation block without ref (the fallback picks the site’s menu, see below) or to ship the items as inner blocks of the Navigation block in the template part; the first edit in the Site Editor then saves them into a new wp_navigation post and adds the ref. For one site’s custom theme, as lumen is, a fixed ref is fine as long as deployments carry the database content along (see Create Block Theme and Version Control).

Submenus and page lists

A Submenu block shows its label and holds child links. With submenuVisibility set to hover, the parent label stays a link and a separate arrow button opens the submenu for keyboard and touch users. With click, the parent label itself becomes a <button> that toggles the submenu, so its own URL can no longer be reached: that is why the Lumen Press About submenu repeats the About page as its first child. The rendered parent in click mode is:

<button data-wp-bind--aria-expanded="state.isSubmenuOpen" data-wp-on--click="actions.toggleMenuOnClick"
  aria-label="About submenu" class="wp-block-navigation-item__content wp-block-navigation-submenu__toggle">
  <span class="wp-block-navigation-item__label">About</span>
</button>

The data-wp-* attributes are Interactivity API directives (see Interactivity API); the Navigation block loads its view script only when it has submenus or an overlay. Inside the overlay, submenus are always expanded.

The Page List block (core/page-list) renders the site’s published pages as a hierarchy and updates itself when pages are published, which makes it the default content of a new Navigation block. Its only attribute, parentPageID, restricts it to the children of one page:

<!-- wp:navigation {"overlayMenu":"never"} -->
<!-- wp:page-list {"parentPageID":4} /-->
<!-- /wp:navigation -->

A page list cannot be reordered or relabelled item by item. Selecting Edit on it in the editor converts it into individual Page Link blocks; from then on, newly published pages are no longer added automatically.

When no menu is selected: the fallback

A Navigation block without ref and without inner blocks does not stay empty. Since 6.3, WP_Navigation_Fallback decides what to render, and it may create a post to do so:

  1. Use the most recently published wp_navigation post.

  2. Otherwise, if the filter wp_navigation_should_create_fallback returns true (the default), convert a classic menu into a new wp_navigation post. The classic menu chosen is the one assigned to the primary location of the active theme, else the menu with the slug primary, else the most recently created classic menu.

  3. Otherwise, create a wp_navigation post titled "Navigation" that contains a Page List.

  4. The filter block_core_navigation_render_fallback receives the resulting blocks and can replace them; returning an empty value renders nothing.

This creation happens on the front end, at the first page view, not only in the editor. On the Lumen Press test site, with a classic menu primary left over from lumen-classic and no wp_navigation post, requesting the home page once was enough:

wp post list --post_type=wp_navigation --fields=ID,post_title,post_name
# ID   post_title   post_name
# 12   Primary      primary

The REST route /wp-block-editor/v1/navigation-fallback runs the same logic and returns the chosen ID ({"id":15} on the finished site); the editor calls it when a Navigation block is inserted.

A site that must not get menus created behind its back, for example one whose content is deployed from version control, disables the creation in a must-use plugin. The block then falls back to a Page List without storing anything:

<?php
/**
 * Plugin Name: Lumen no navigation fallback
 * Description: Never create wp_navigation posts automatically.
 */

add_filter( 'wp_navigation_should_create_fallback', '__return_false' );

Importing classic menus

Classic menus are converted, never moved: the original nav_menu term and its items stay untouched, so a site can switch back to its classic theme. There are three ways:

  • In the editor. Select the Navigation block, open the Options menu next to the menu name in the List View panel, and choose a menu under Import classic menus (the group appears only when classic menus exist). The block gets a new wp_navigation post with the same title and the ref is updated.

  • Automatically, through the fallback described above.

  • With a script, when a migration must be repeatable. The converter class that the fallback uses, WP_Classic_To_Block_Menu_Converter (6.3), is public.

The classic Lumen Press menu was built on the lumen-classic theme with WP-CLI:

wp menu create "Primary" --porcelain
# 2
wp menu item add-custom primary "Books" https://lumenpress.test/books/ --porcelain
wp menu item add-post primary 4 --porcelain          # About, prints 9
wp menu item add-post primary 6 --parent-id=9        # Submissions under About
wp menu item add-post primary 5                      # Contact
wp menu location assign primary primary
wp menu item list primary --fields=db_id,title,menu_item_parent
# db_id  title        menu_item_parent
# 8      Books        0
# 9      About        0
# 10     Submissions  9
# 11     Contact      0

Source: WP-CLI: wp menu

The script below copies a classic menu into a new wp_navigation post. It is run with wp eval-file, which passes the extra arguments in $args:

<?php
/**
 * Copies a classic menu into a new wp_navigation post.
 * Usage: wp eval-file import-classic-menu.php <menu-slug>
 */

$slug = isset( $args[0] ) ? $args[0] : 'primary';
$menu = wp_get_nav_menu_object( $slug );

if ( ! $menu ) {
	WP_CLI::error( "No classic menu with slug '{$slug}'." );
}

$blocks = WP_Classic_To_Block_Menu_Converter::convert( $menu );

if ( is_wp_error( $blocks ) || '' === $blocks ) {
	WP_CLI::error( 'The classic menu could not be converted.' );
}

$id = wp_insert_post(
	array(
		'post_type'    => 'wp_navigation',
		'post_status'  => 'publish',
		'post_title'   => $menu->name,
		'post_name'    => $menu->slug,
		'post_content' => $blocks,
	),
	true
);

if ( is_wp_error( $id ) ) {
	WP_CLI::error( $id );
}

WP_CLI::success( "Created wp_navigation post {$id} from '{$menu->name}'." );
wp eval-file import-classic-menu.php primary
# Success: Created wp_navigation post 14 from 'Primary'.
wp eval-file import-classic-menu.php nosuch
# Error: No classic menu with slug 'nosuch'.

The converter turns items with children into Submenu blocks and the rest into Navigation Link blocks, keeps labels, URLs, object IDs, title, description, rel and "open in new tab", and copies the classic CSS classes into the block’s className (for example menu-item menu-item-type-post_type menu-item-object-page). Those classes are kept so that old CSS keeps matching; they are dead weight once the theme’s styles move to theme.json and can be removed in the editor. Every URL is copied as an absolute URL as it is at conversion time (page links get their current permalink), so a menu converted on a staging host carries that host into the new post and needs a search-and-replace when the content moves to production.

Navigation overlays (7.0)

Below 600px (or at every width with overlayMenu set to always), the Navigation block collapses behind an open button and shows its items in an overlay. Until 6.9 that overlay could only be recoloured. Since 7.0 the overlay can be a template part in the navigation-overlay area, designed with any blocks: a logo, the menu, a search field, social links, a call to action.

The area is a fourth template-part area next to header, footer and uncategorized (constant WP_TEMPLATE_PART_AREA_NAVIGATION_OVERLAY). A theme must declare its overlay part in theme.json; a part without that declaration gets the uncategorized area and the Navigation block does not offer it as an overlay:

{
	"$schema": "https://schemas.wp.org/wp/7.1/theme.json",
	"version": 3,
	"templateParts": [
		{ "name": "header", "title": "Header", "area": "header" },
		{ "name": "footer", "title": "Footer", "area": "footer" },
		{ "name": "mobile-menu", "title": "Mobile menu", "area": "navigation-overlay" }
	]
}

The part itself, parts/mobile-menu.html, uses the Navigation Overlay Close block (core/navigation-overlay-close, 7.0) for the close button and a second Navigation block for the items:

<!-- wp:group {"style":{"spacing":{"padding":{"top":"var:preset|spacing|40","right":"var:preset|spacing|40","bottom":"var:preset|spacing|40","left":"var:preset|spacing|40"}}},"backgroundColor":"ink","textColor":"paper","layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group has-paper-color has-ink-background-color has-text-color has-background">
	<!-- wp:navigation-overlay-close {"displayMode":"both","text":"Close menu"} /-->
	<!-- wp:site-title {"level":0} /-->
	<!-- wp:navigation {"ref":15,"layout":{"type":"flex","orientation":"vertical"}} /-->
	<!-- wp:search {"label":"Search the catalogue","buttonText":"Search"} /-->
</div>
<!-- /wp:group -->

The header’s Navigation block names the part by slug in its overlay attribute ("overlay":"mobile-menu"). The dev note asks for the bare slug; a theme//slug value is still read for compatibility, and a part from another theme is ignored. In the editor the same link is made with Create overlay in the block’s Overlay template panel.

What the 7.1.3 renderer does with it, verified on the page output:

  • The part’s blocks are printed inside <div class="wp-block-navigation__overlay-container">, and the responsive container gets the class disable-default-overlay, so the overlay colour settings of the Navigation block no longer apply: the part styles itself.

  • A Navigation block inside the overlay is forced to overlayMenu never (no overlay inside the overlay) and renders as a <div> with the menu title as aria-label, not as a second <nav> landmark.

  • The Navigation Overlay Close block renders a <button type="button">. Its displayMode is icon (default; the button then gets aria-label="Close"), text or both, and text defaults to "Close". When the part contains this block, the default close button is not printed; when it does not, WordPress adds its own close button so the overlay can always be closed.

  • Images in the overlay get fetchpriority="low" because they are hidden until the menu opens.

Overlay parts are full-screen in 7.0 and 7.1; a slide-in drawer needs custom CSS, and a general dialog block is still in development. Like every template part, an overlay belongs to the active theme and is not carried over on a theme switch. A theme can offer ready-made overlay designs as patterns scoped to the area with a Block Types: core/template-part/navigation-overlay header; core 7.1 registers five of its own (core/navigation-overlay, core/navigation-overlay-black-bg, core/navigation-overlay-accent-bg, core/navigation-overlay-centered, core/navigation-overlay-centered-with-extras):

<?php
/**
 * Title: Paper overlay
 * Slug: lumen/overlay-paper
 * Categories: navigation
 * Block Types: core/template-part/navigation-overlay
 * Description: Light overlay menu with a text close button.
 */
?>
<!-- wp:group {"backgroundColor":"paper","textColor":"ink","layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group has-ink-color has-paper-background-color has-text-color has-background">
	<!-- wp:navigation-overlay-close {"displayMode":"text","text":"<?php echo esc_attr__( 'Close', 'lumen' ); ?>"} /-->
	<!-- wp:navigation {"layout":{"type":"flex","orientation":"vertical"}} /-->
</div>
<!-- /wp:group -->

The Navigation block in this pattern has no ref, so it resolves through the fallback to the site’s most recent menu, which keeps the pattern portable. Theme pattern files are cached; during development set WP_DEVELOPMENT_MODE to theme (or call wp_get_theme()->delete_pattern_cache()) so that edits to the header comment are picked up. Pattern files are covered in Theme Patterns.

Styling menus in theme.json

Navigation colours, typography and spacing are set like those of any block, under styles.blocks. WordPress 7.1 adds state styles for the Navigation Link block: the pseudo states :hover, :focus, :focus-visible and :active (also available for Button), and the custom state -current for the item that links to the page being viewed. The -current state exists only in theme.json; the editor has no control for it. Pseudo states can be nested inside it:

{
	"$schema": "https://schemas.wp.org/wp/7.1/theme.json",
	"version": 3,
	"styles": {
		"blocks": {
			"core/navigation-link": {
				"typography": { "textDecoration": "none" },
				":hover": {
					"typography": { "textDecoration": "underline" }
				},
				"-current": {
					"color": { "text": "var(--wp--preset--color--lumen-red)" },
					"typography": { "fontWeight": "700" },
					":hover": {
						"color": { "text": "var(--wp--preset--color--ink)" }
					}
				}
			}
		}
	}
}

The selector for -current comes from the block’s block.json (selectors.states), which is .wp-block-navigation .current-menu-item. The generated global stylesheet contains:

:root :where(.wp-block-navigation-link){text-decoration: none;}
:root :where(.wp-block-navigation-link:hover){text-decoration: underline;}
:root :where(.wp-block-navigation .current-menu-item){color: var(--wp--preset--color--lumen-red);font-weight: 700;}
:root :where(.wp-block-navigation .current-menu-item:hover){color: var(--wp--preset--color--ink);}

On the rendered page, the <li> of the current item gets current-menu-item and its link gets aria-current="page"; the parent Submenu gets current-menu-ancestor, which -current does not match. A theme that also wants to mark the section in the top level adds a rule for .current-menu-ancestor in its stylesheet. The two states are separate from the viewport states @mobile and @tablet, also new in 7.1; all are described in theme.json Styles.

Before 7.1 a theme needed hand-written CSS for these states, and older tutorials still target classic classes such as .current_page_item, which the Navigation block does not output.

Classic menus: register_nav_menus() and wp_nav_menu()

A classic or hybrid theme declares locations and prints menus in PHP. This is the whole menu code of lumen-classic; the rest of a classic theme is covered in Classic Theme Anatomy and The Loop and functions.php, Theme Supports and Assets.

<?php
/**
 * Lumen Classic: menu locations (functions.php).
 */

function lumen_classic_register_menus() {
	register_nav_menus(
		array(
			'primary' => __( 'Primary menu', 'lumen' ),
			'footer'  => __( 'Footer menu', 'lumen' ),
		)
	);
}
add_action( 'after_setup_theme', 'lumen_classic_register_menus' );
<header class="site-header">
	<?php
	if ( has_nav_menu( 'primary' ) ) {
		wp_nav_menu(
			array(
				'theme_location'       => 'primary',
				'container'            => 'nav',
				'container_class'      => 'site-nav',
				'container_aria_label' => __( 'Main', 'lumen' ),
				'menu_class'           => 'site-nav__list',
				'depth'                => 2,
				'fallback_cb'          => false,
			)
		);
	}
	?>
</header>

On the About page this prints <nav class="site-nav" aria-label="Main">, an <li> with current-menu-item and current_page_item, aria-current="page" on the link, and a nested <ul class="sub-menu">. Without fallback_cb => false, an unassigned location prints wp_page_menu(), a list of all pages, which is the classic counterpart of the Page List fallback. has_nav_menu() avoids an empty <nav>.

The differences that matter when moving a theme from one model to the other:

  • Menu locations do not exist in a block theme; the template part that contains the Navigation block is the location. register_nav_menus() in a block theme brings back Appearance > Menus, but nothing prints those menus unless PHP code calls wp_nav_menu().

  • Classic submenus are opened with CSS or theme JavaScript; the Navigation block ships its own keyboard-aware behaviour.

  • A Walker_Nav_Menu subclass that changes classic markup has no block equivalent; the block markup is changed with the render_block_core/navigation-link filter or with Block Hooks (see Block Hooks).

  • Classic themes are not dead: a classic theme keeps the Menus screen and the Customizer menu panel, and the block widget editor (5.8) does not change menus. The step-by-step move of lumen-classic to lumen is in Hybrid Themes and Migration to Block Themes.

Accessible menus

A menu is the main way through a site, so its accessibility failures affect every page. The Navigation block covers the mechanics; the theme and the site owner remain responsible for the rest.

What the block does in 7.1.3:

  • It renders a <nav> landmark with an aria-label: the block’s ariaLabel if set, otherwise the menu’s title. When a page has more than one <nav> (header and footer), each needs a distinct label, such as "Main" and "Footer"; WordPress makes duplicate menu titles unique, but meaningful labels are the theme’s job.

  • Items are a <ul> list of links; the current item gets aria-current="page".

  • Submenu toggles are <button> elements with aria-expanded and an aria-label such as "About submenu", following the disclosure navigation pattern. Hover submenus also open on focus, and Escape closes an open submenu.

  • The open button of the overlay has aria-haspopup="dialog" and the label "Open menu"; the open overlay gets role="dialog" and aria-modal="true", focus moves into it and is kept there, and Escape closes it and returns focus to the open button.

What the theme and the site owner must still check:

  • Close control. A custom overlay part should contain a Navigation Overlay Close block with a visible label or the icon mode’s aria-label; WordPress adds a fallback button otherwise, but it may sit where users do not expect it.

  • Focus visibility. Removing outlines in custom CSS breaks keyboard use; use the :focus-visible state in theme.json to style focus rather than hide it.

  • Contrast. The overlay is a separate background: check text and link contrast in the overlay part and in submenus (at least 4.5:1 for text), and for the -current and :hover colours too. Colour must not be the only marker of the current item, which is why the Lumen Press -current style also sets the weight.

  • Click versus hover. Hover-only menus fail on touch screens; submenuVisibility click is more predictable, at the cost of making the parent label a button (repeat the parent page as a child, as above).

  • Labels. Icon-only items (social links, a search icon) need text alternatives; link text should make sense out of context ("Submissions", not "Click here"); avoid opening menu links in a new tab.

  • Depth and size. Deep submenus are hard to use with a keyboard and on phones; two levels are usually enough.

  • Skip link. Block themes get a skip link to the main content automatically when a template has a <main> element; keep the tagName main on the content group so that keyboard users can jump past the menu.

The accessibility-ready requirements for themes in the theme directory, and how to test them, are in Theme Accessibility and Releasing; general web techniques are in Accessibility.

What older books get wrong or leave out

The older books describe menus only through the classic model or through a 6.3-era block editor:

  • A book centred on classic themes teaches register_nav_menus(), wp_nav_menu() and Appearance > Menus as the only way to build menus. That remains correct for classic themes, but with a block theme the Menus screen is absent and menus are Navigation blocks stored as wp_navigation posts.

  • A book centred on the Customizer edits menus in Customizer > Menus; the Customizer is hidden for block themes, and the menu is edited in the Site Editor instead.

  • A site-owner book written for 6.3–6.4 shows the Navigation block before the 7.0 overlay templates, the submenuVisibility setting with its always option, and the 7.1 Overlay panel and state styles; its description of the mobile menu (colours only) is outdated.

  • None of the books mentions that the Navigation block can create wp_navigation posts on the front end from classic menus, or that a ref copied to another site can silently render an empty menu.

Common errors and pitfalls

Symptom Cause and fix

The header shows no menu items and no error

ref points to a post that does not exist on this site (copied theme, imported template part). Select the menu again in the editor or remove ref.

The header shows a different menu than expected

ref points to a draft or trashed menu, so the fallback shows the most recent published menu. Publish the intended menu.

New wp_navigation posts named "Primary" or "Navigation" appear

The fallback created them on a page view. Select a menu explicitly or add the wp_navigation_should_create_fallback filter.

The custom overlay is not offered or not used

The part is not declared in theme.json with "area": "navigation-overlay", or the overlay value is not the part’s slug.

The overlay has two close buttons or none visible

A custom close inside a nested pattern or a styled-away default button. Keep exactly one Navigation Overlay Close block in the part and give it contrast.

The parent of a submenu cannot be visited

submenuVisibility is click, which turns the parent into a button. Add the parent page as the first child.

A -current style does not apply to the section heading

-current matches .current-menu-item only; the parent Submenu gets current-menu-ancestor.

Old theme CSS for .current_page_item or .sub-menu has no effect

Those are classic wp_nav_menu() classes. Use the block classes or theme.json states.

Editors cannot see Navigation in the Site Editor

wp_navigation requires edit_theme_options, which only Administrators have by default.