@wordpress/edit-navigation
Edit
Edit Navigation page module for WordPress – a Gutenberg-based UI for editing navigation menus.
This package is meant to be used only with WordPress core. Feel free to use it in your own project but please keep in mind that it might never get fully documented.
Usage
/**
* WordPress dependencies
*/
import { initialize } from '@wordpress/edit-navigation';
/**
* Internal dependencies
*/
import blockEditorSettings from './block-editor-settings';
initialize( '#navigation-editor-root', blockEditorSettings );
Purpose
By default, the Navigation Editor screen allows users to create and edit complex navigations using a block-based UI. The aim is to supersede the current Menus screen by providing a superior experience whilst retaining backwards compatibility.
The editing experience is provided as a block editor wrapper around the core functionality of the Navigation block. Features of the block are disabled/enhanced as necessary to provide an experience appropriate to editing a navigation outside of a Full Site Editing context.
Modes
The Navigation Editor has two “modes” for persistence (“saving” navigations) and rendering:
- Classic (default) – navigations are saved to the existing (post type powered) Menus system and rendered using standard Walker classes.
- Block-based (opt in) – navigations continue to be saved using the existing post type system, but:
- the navigation is rendered using the
core/navigation
block (as opposed to Walker) to provide access to the full power of blocks (with some tradeoffs in terms of backwards compatibility). - non-link blocks (anything that is not
core/navigation-link
) are saved as blocks.
- the navigation is rendered using the
Classic Mode
In this mode, navigations created in the Navigation Editor are stored using the existing Menu post type (nav_menu_item
) system. As this method matches that used in the existing Menus screen, there is a smooth upgrade path to using new Navigation Editor screen to edit navigations.
Moreover, when the navigation is rendered on the front of the site the system continues to use the classic Navigation “Walker” class, thereby ensuring the HTML markup remains the same when using a classic Theme.
Block-based Mode
Important: block-based mode has been temporarily disabled until it becomes stable. So, if a theme declares support for the block-nav-menus
feature it will not affect the frontend.
If desired, themes are able to opt into rendering complete block-based menus using the Navigation Editor. This allows for arbitrarily complex navigation block structures to be used in an existing theme whilst still ensuring the navigation data is still saved to the existing (post type powered) Menus system.
Themes can opt into this behaviour by declaring:
add_theme_support( 'block-nav-menus' );
This unlocks significant additional capabilities in the Navigation Editor. For example, by default, the Navigation Editor screen only allows link (core/navigation-link
) blocks to be inserted into a navigation. When a theme opts into block-nav-menus
however, users are able to add non-link blocks to a navigation using the Navigation Editor screen, including:
core/navigation-link
.core/social
.core/search
.
As these items are still saved to nav_menu_items
this ensures if we ever revert to classic (Walker-based) rendering, these items will still be rendered (as blocks).
Backwards compatibility
By design the underlying systems of the Nav Editor screen should be largely backwards compatible with the existing Menus screen. Therefore any navigations created or edited using the new Navigation Editor screen should continue to work in the existing classic Menus screen.
Currently, the only exception to this would be any custom functionality added (by Plugins or otherwise) to the existing Menus screen would not be replicated in the new Navigation Editor screen. In this scenario there might be danger of some data loss.
Downgrading from block-based to classic Themes
If the user switches to a theme that does not support block menus, or disables this functionality, ~non-link blocks are no longer rendered on the frontend~ block-based links will still be rendered on the front end. Care is also taken to ensure that users can still see their data on the existing Menus screen.
Block to Menu Item mapping
The Navigation Editor needs to be able to map navigation items in two directions:
nav_menu_item
s to Blocks – when displaying an existing navigation.- Blocks to
nav_menu_item
s – when saving an navigation being editing in the Navigation screen.
The Navigation Editor has two dedicated methods for handling mapping between these two expressions of the data:
To understand these fully, one must appreciate that WordPress maps raw nav_menu_item
posts to Menu item objects. These have various properties which map as follows:
Menu Item object property | Equivalent Block Attribute | Description |
---|---|---|
ID |
Not mapped. | The term_id if the menu item represents a taxonomy term. |
attr_title |
title |
The title attribute of the link element for this menu item. |
classes |
classNames |
The array of class attribute values for the link element of this menu item. |
db_id |
Not mapped. | The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn’t exist). |
description |
description |
The description of this menu item. |
menu_item_parent |
Not mapped.1 | The DB ID of the nav_menu_item that is this item’s menu parent, if any. 0 otherwise. |
object |
type |
The type of object originally represented, such as ‘category’, ‘post’, or ‘attachment’. |
object_id |
id |
The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories. |
post_parent |
Not mapped. | The DB ID of the original object’s parent object, if any (0 otherwise). |
post_title |
Not mapped. | A “no title” label if menu item represents a post that lacks a title. |
target |
opensInNewTab 2 |
The target attribute of the link element for this menu item. |
title |
label |
The title of this menu item. |
type |
kind |
The family of objects originally represented, such as ‘post_type’ or ‘taxonomy’. |
type_label |
Not mapped. | The singular label used to describe this type of menu item. |
url |
url |
The URL to which this menu item points. |
xfn |
rel |
The XFN relationship expressed in the link of this menu item. |
\_invalid |
Not mapped. | Whether the menu item represents an object that no longer exists. |
- [1] – the parent -> child relationship is expressed in block via the
innerBlocks
attribute and is therefore not required as a explicit block attribute. - [2] – applies only if the value of the
target
field is_blank
.
Inconsistencies
Mapping
For historical reasons, the following properties display some inconsistency in their mapping from Menu Item Object to Block attribute:
type
->kind
– the family of objects is stored askind
on the block and so must be mapped accordingly.object
->type
– the type of object is stored astype
on the block and so must be mapped accordingly.object_id
->id
– the block stores a reference to the original object’s ID as theid
attribute. This should not be confused with the block’sclientId
which is unrelated.attr_title
->title
– the HTMLtitle
attribute is stored astitle
on the block and so must be mapped accordingly.
Object Types
- Menu Item objects which represent “Tags” are stored in WordPress as
post_tag
but the block expects theirtype
attribute to betag
(omiting thepost_
suffix). This inconsistency is accounted for in the mapping utilities methods.
Hooks
The useNavigationEditor
and useEntityBlockEditor
hooks are the central part of this package. They bridge the gap between the API and the block editor interface:
// Data from API:
const {
menus,
hasLoadedMenus,
selectedMenuId,
navigationPost,
} = useNavigationEditor();
// Working state:
const [ blocks, onInput, onChange ] = useEntityBlockEditor(
NAVIGATION_POST_KIND,
NAVIGATION_POST_POST_TYPE,
{
id: navigationPost?.id,
}
);
const isBlockEditorReady = !! (
menus?.length &&
navigationPost &&
selectedMenuId
);
return (
<BlockEditorProvider
value={ blocks }
onInput={ onInput }
onChange={ onChange }
settings={ blockEditorSettings }
>
{ isBlockEditorReady && (
<div className="edit-navigation-layout__content-area">
<BlockTools>
<Editor isPending={ ! hasLoadedMenus } />
</BlockTools>
</div>
) }
</BlockEditorProvider>
);
Glossary
- (Navigation) link – the basic
core/navigation-link
block which is the standard block used to add links within navigations. - Block-based link – any navigation item that is not a
core/navigation-link
block. These are persisted as blocks but still utilise the existing Menus post type system. - Navigation block – the root
core/navigation
block which can be used both with the Navigation Editor and outside (eg: Post / Site Editor). - Navigation editor / screen – the new screen provided by Gutenberg to allow the user to edit navigations using a block-based UI.
- Menus screen – the current/existing interface/screen for managing Menus in WordPress WPAdmin.
This package assumes that your code will run in an ES2015+ environment. If you’re using an environment that has limited or no support for such language features and APIs, you should include the polyfill shipped in @wordpress/babel-preset-default
in your code.
Contributing to this package
This is an individual package that’s part of the Gutenberg project. The project is organized as a monorepo. It’s made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to npm and used by WordPress as well as other software projects.
To find out more about contributing to this package or Gutenberg as a whole, please read the project’s main contributor guide.