Skip to main content

Routing and Navigation

Role

ReactWP uses React Router for browser history, but WordPress still owns the route payload.

That means navigation is split into two layers:

  • React Router keeps the browser URL and history in sync
  • ReactWP fetches, caches, and renders the actual route payload from WordPress

Main Files

  • src/themes/reactwp/js/inc/RouteService.js
  • src/themes/reactwp/js/inc/useRouteTransition.js
  • src/themes/reactwp/js/components/AppLink.jsx
  • src/mu-plugins/plugins/reactwp/template/inc/routes/rest.php

Direct Load vs Client Navigation

On a direct page load:

  1. WordPress resolves the current object
  2. RouteResolver turns it into a route payload
  3. Bootstrap injects that route into the initial JSON payload
  4. React mounts from that payload

On client-side navigation:

  1. React Router detects the requested location
  2. ReactWP asks the REST route endpoint for the normalized route payload
  3. the loader prepares the critical assets
  4. the page transition runs
  5. the new route is rendered without a full page reload

Route Fetching

The REST endpoint lives at:

  • reactwp/v1/route

The frontend usually reaches it through:

  • runtime.system.routeEndpoint

RouteService.js normalizes the returned payload so the runtime always gets a predictable route shape.

Route Cache

RouteService.js keeps an in-memory Map keyed by normalized pathname plus normalized search string.

That means:

  • revisiting a page in the same session can reuse the existing payload
  • a hover-prefetched route can be available faster on click
  • back and forward navigation can also benefit from the same cached payload

Hashes are not part of route identity. ReactWP treats:

  • pathname + search as the route key
  • hash as local scroll state

Important Cache Limitation

The route cache is session memory, not a live invalidation system.

If the content of a page changes in WordPress while the app is already open, a previously cached route can stay stale until:

  • the page is fully reloaded
  • the tab is reopened
  • the cache is cleared by a future runtime change

ReactWP does not currently ship automatic route cache invalidation.

The ReactWP cache generation rotates Cache Storage and initial asset URLs, but an already-running tab still keeps its route memory until a full page load. Changing the server generation cannot mutate JavaScript memory in a remote open tab.

Prefetching

AppLink.jsx prefetches routes on:

  • mouse enter
  • focus

It does that by calling Loader.prepareRoute(...), which means the route payload and its critical asset preparation can already be in flight before the click happens.

Internal Navigation Outside #app

ReactWP also listens for clicks on internal anchors outside #app.

This matters for elements such as:

  • portal-mounted header content
  • other shell-level anchors rendered outside the main React tree

That behavior lives in useInternalNavigation.js.

If an anchor outside #app points to an internal URL, ReactWP can intercept it and turn it into a client-side navigation.

Forcing A Full Browser Navigation

If you want a shell-level anchor outside #app to bypass React navigation, set:

data-router="false"

on that raw anchor element.

This tells the internal navigation listener to leave the click alone so the browser can perform a normal page load.

History Arrows

Back and forward navigation use the same ReactWP route transition flow as normal internal links.

That is why the route transition layer and the route cache both matter for:

  • normal clicks
  • browser back
  • browser forward

Query Strings and Hashes

ReactWP normalizes query strings through URLSearchParams. Repeated query keys become arrays in route.query.

A location such as /projects/?type=house#results is treated as:

  • route identity: /projects/?type=house
  • local scroll target: #results

Hash-only links on the current route bypass the leave/enter transition. ReactWP refreshes the scroller and scrolls to the target after the browser frame is ready.

By default, a same-page AppLink or internal Button also writes the target hash to the URL and adds a browser history entry. Pass updateHash={false} when the click should scroll to the target without changing the visible URL. This option does not remove the link's href; it only changes how a normal click updates history. See Components for examples.

Use these components when you want standard ReactWP navigation behavior:

  • AppLink for internal links
  • Button with to or internal href for navigation actions

Use a raw anchor only when you intentionally want browser-native navigation behavior.