Navigation Menus as a REST API Endpoint
Adds a public /wp-json/site/v1/menu/{location} endpoint that returns the menu assigned to a theme location as a nested list, ready for a headless or app front end.
Opens WPIDE on
Code
add_action( 'rest_api_init', function () {
register_rest_route( 'site/v1', '/menu/(?P<location>[a-z0-9_-]+)', array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => '__return_true', // Menus are public on the site anyway.
'callback' => function ( WP_REST_Request $request ) {
$locations = get_nav_menu_locations();
$location = $request['location'];
if ( empty( $locations[ $location ] ) ) {
return new WP_Error( 'no_menu', 'No menu is assigned to this location.', array( 'status' => 404 ) );
}
$items = wp_get_nav_menu_items( $locations[ $location ] );
if ( ! $items ) {
return rest_ensure_response( array() );
}
$by_parent = array();
foreach ( $items as $item ) {
$by_parent[ (int) $item->menu_item_parent ][] = $item;
}
$build = function ( $parent_id ) use ( &$build, $by_parent ) {
$out = array();
foreach ( $by_parent[ $parent_id ] ?? array() as $item ) {
$out[] = array(
'id' => (int) $item->ID,
'title' => wp_strip_all_tags( $item->title ),
'url' => $item->url,
'target' => $item->target,
'classes' => array_values( array_filter( (array) $item->classes ) ),
'children' => $build( (int) $item->ID ),
);
}
return $out;
};
return rest_ensure_response( $build( 0 ) );
},
) );
} );
Notes
Works with classic menu locations (Appearance > Menus); block themes that only use the Navigation block have no locations to return.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.