Skip to main content

Content and Menus

Content Modeling

ReactWP expects WordPress and ACF to define the content model.

WordPress provides:

  • pages
  • posts
  • taxonomies
  • menus

ACF provides:

  • structured route data
  • site settings
  • theme-level configuration fields

Menu locations are driven by the ReactWP site settings rather than being forced directly in the theme.

That means the starter can ship a default menu location while still allowing the project owner to rename or remove locations from the admin.

The runtime reads those locations from the Site settings options page and registers them with WordPress automatically.

The location slug becomes part of the frontend contract, because React templates consume menus through keys such as navigation.primary.

Default Starter Data

On first boot, ReactWP seeds:

  • a starter home page
  • default language rows
  • starter menu location rows
  • a starter navigation menu assigned to the default location

By default, the first boot creates a primary location and assigns a starter menu to it.

That starter row is not hardcoded forever. It is just seeded as a default and can be removed or renamed later from Site settings.

How To Add A Menu Location

ReactWP expects menu locations to be created from the admin, not hardcoded in the theme.

Typical flow:

  1. Open ReactWP -> Site settings
  2. Find the Theme Locations repeater
  3. Add a new row
  4. Set a slug
  5. Fill the translated names such as name_fr and name_en
  6. Save the settings page

After that, ReactWP registers the new location with WordPress.

How To Assign A Menu To That Location

Once the location exists:

  1. Open Appearance -> Menus
  2. Create or edit a WordPress menu
  3. Assign it to the location you created
  4. Save the menu

ReactWP will then expose that location in the frontend bootstrap payload.

Frontend Navigation Shape

Menus are normalized by the PHP runtime before they reach React.

The frontend receives navigation data through the bootstrap payload, so templates and components can consume navigation without rebuilding raw WordPress menu structures.

Each location becomes an array of normalized items.

Each item can include:

  • id
  • label
  • title
  • url
  • path
  • target
  • classes
  • children

The children value is already normalized as a tree, so dropdown or nested menu rendering does not need to rebuild parent-child relationships manually in React.

How To Use A Menu In A Template

Templates receive navigation as a prop from the React app shell.

Example:

const ExampleTemplate = ({ navigation }) => {
const primaryMenu = navigation.primary || [];

return (
<nav className="site-nav">
{primaryMenu.map((item) => (
<a key={item.id} href={item.url}>
{item.label}
</a>
))}
</nav>
);
};

For React navigation and prefetching, use AppLink or Button instead of a raw anchor:

import AppLink from '../components/AppLink';

const ExampleTemplate = ({ navigation }) => {
const primaryMenu = navigation.primary || [];

return (
<nav className="site-nav">
{primaryMenu.map((item) => (
<AppLink key={item.id} to={item.path}>
{item.label}
</AppLink>
))}
</nav>
);
};

How To Use A Menu In The Header

The header is not rendered as a page template.

It is a shell-level component mounted by AppShell, so it does not receive navigation automatically the same way a page template does.

If you want a header menu, pass navigation through headerProps from App.jsx.

Example:

import { runtime } from './inc/Runtime';

<AppShell
showHeader={true}
headerProps={{
className: 'site-header',
navigation: runtime.navigation,
site: runtime.site,
theme: runtime.theme,
system: runtime.system
}}
showFooter={false}
>
<Outlet />
</AppShell>

Then your header component can pass that data to a dedicated navigation component:

const Header = ({ navigation = {}, ...props }) => {
const primaryMenu = navigation.primary || [];

return (
<header {...props}>
<nav>
{primaryMenu.map((item) => (
<AppLink key={item.id} to={item.path}>
{item.label}
</AppLink>
))}
</nav>
</header>
);
};

This is the recommended pattern because:

  • page templates already receive navigation automatically
  • shell components like Header do not
  • App.jsx is the correct place to distribute shared shell props

If you import runtime.navigation directly inside Header.jsx, that also works, but passing it through headerProps is clearer and easier to follow.

Nested Menus

Menu items are already normalized as a tree.

That means child items live in item.children, so nested rendering can be handled recursively if the project needs dropdowns or multi-level navigation.

Where This Data Comes From

Menu normalization happens in the PHP runtime, mainly through:

  • inc/runtime/MenuBuilder.php
  • inc/runtime/Bootstrap.php

That is why frontend code can work with navigation.primary directly instead of reconstructing raw wp_get_nav_menu_items() data.