Skip to main content

Frontend Runtime

Role

The frontend runtime mounts the React app, reads the bootstrap payload, resolves templates, prepares assets, coordinates navigation, and re-syncs document head state after client-side route changes.

Main Files

  • src/themes/reactwp/js/App.jsx
  • src/themes/reactwp/js/inc/Runtime.js
  • src/themes/reactwp/js/inc/Loader.js
  • src/themes/reactwp/js/inc/PageTransition.js
  • src/themes/reactwp/js/inc/RouteService.js
  • src/themes/reactwp/js/inc/TemplateRegistry.js
  • src/themes/reactwp/js/inc/Scroller.js
  • src/themes/reactwp/js/inc/Cache.js
  • src/themes/reactwp/js/inc/AnimationLifecycle.js
  • src/themes/reactwp/js/inc/motion.js
  • src/themes/reactwp/js/inc/useRouteTransition.js
  • src/themes/reactwp/js/inc/AppShell.jsx

Project-Facing APIs

ReactWP exposes a small set of frontend surfaces intended for normal project customization:

APIImportPurpose
runtimeinc/Runtimenormalized bootstrap values and initial route
registerTemplate, registerTemplatesinc/TemplateRegistryconnect WordPress template names to lazy React modules
Loaderinc/Loaderconfigure initial animation and prepare route assets
PageTransitionAnimationinc/PageTransitionconfigure leave/enter behavior
scrollerinc/Scrollerinitialize, refresh, scroll, lock, unlock, or kill the smoother facade
gsap, ScrollTriggerinc/motionuse the same registered motion dependencies as the runtime
AppLink, Buttoncomponents/navigation-aware links and commands
Image, Video, Audiocomponents/loader-managed media placeholders

Prefer the files under inc/config/ for one-time project configuration. They keep project choices separate from reusable runtime mechanics.

Runtime Payload

Runtime.js reads the JSON payload injected by PHP and exposes it through runtime.

Common values:

  • runtime.bootstrap
  • runtime.site
  • runtime.system
  • runtime.assets
  • runtime.navigation
  • runtime.currentUser
  • runtime.route
  • runtime.theme
  • runtime.seoDefaults

For the full payload contract, see Bootstrap and Route Payloads.

Example

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

console.log(runtime.site.name);
console.log(runtime.system.routeEndpoint);
console.log(runtime.route.template);

App Entry

App.jsx is kept intentionally small.

It:

  • initializes the template registry
  • mounts the router
  • hydrates valid static/server HTML or creates the client root
  • resolves the active React template
  • passes route readiness back to the route transition hook
  • wraps the current route in AppShell

Most navigation orchestration lives in useRouteTransition.js, not in the app entry itself.

Router Model

ReactWP uses the browser APIs provided directly by React Router, but the route payload itself comes from WordPress.

That means:

  • React Router owns browser history integration
  • ReactWP owns the actual route payload and template selection
  • currentRoute in useRouteTransition.js is the runtime source of truth for template data

This is why a route change is more than a simple component switch. It includes payload fetch, asset preparation, route readiness, and animation timing.

Template Resolution

TemplateRegistry.js contains the registry mechanics.

Projects should usually register or override templates through:

  • src/themes/reactwp/js/inc/config/configureTemplateRegistry.js

That keeps the runtime implementation separate from project-level template decisions.

Template Props

When ReactWP resolves a route template, it passes a small standard prop set to the template component.

Current template props are:

  • route
  • site
  • theme
  • system
  • navigation
  • currentUser

Example:

const Default = ({ route, site, theme, system, navigation, currentUser }) => {
return (
<div>
<h1>{route.data.hero_title}</h1>
<p>{site.name}</p>
</div>
);
};

Notes:

  • route is the most important prop and contains the active route payload, including values such as path, search, query, template, data, seo, and head
  • site, theme, system, and navigation come from the shared bootstrap runtime
  • currentUser is the integrated WordPress user payload and always includes authenticated
  • if a project needs more standard props for every template, App.jsx is the place where those props are injected

For language-aware rendering, prefer the current route value and fall back to the initial bootstrap value:

const language = route.lang || site.language || 'en';
const locale = site.locale || 'en_US';

runtime.site.language is available to low-level modules, but it represents the initial bootstrap and does not replace route.lang when navigation can switch languages.

Loader and Preloading

Loader.js owns the first-load lifecycle.

It is responsible for:

  • animating the initial loader
  • preloading the next template
  • loading critical fonts and media before a route becomes visible
  • starting non-critical media downloads after the route is ready

Projects should customize loader behavior through:

  • src/themes/reactwp/js/inc/config/configureLoader.js

See Loader.

Page Transitions

PageTransition.js handles only the route transition animation layer.

By default, it fades #viewport out and back in while useRouteTransition.js coordinates:

  • fetching the next route
  • waiting for critical assets
  • swapping the current route
  • waiting for the next view to mount
  • revealing the new view

Projects should customize this through:

  • src/themes/reactwp/js/inc/config/configurePageTransition.js

See Page Transitions.

Route Fetching

RouteService.js only fetches and normalizes route payloads.

The route hook and the loader decide when those routes should be prepared, revealed, and followed by deferred downloads.

RouteService.js also keeps an in-memory cache of visited route payloads for the current session, keyed by normalized pathname plus normalized search string.

See Routing and Navigation.

Browser Cache Runtime

Cache.js provides generic versioned JSON and media Cache Storage helpers. Its generation comes from runtime.system.cacheVersion.

On initialization it:

  • opens the active JSON and media cache names
  • removes stale ReactWP generations
  • adds the cache generation to same-origin media request URLs
  • falls back cleanly when Cache Storage is unavailable

Cache.js exports one ReactWPCache object with this complete project-callable surface:

MethodResult
initialize()delete stale managed JSON/media generations once and return the cleanup Promise
media(url, options)return a cached/in-flight blob URL by default; { asBlob: false } returns the original URL, and { useCache: false } bypasses Cache Storage
json(url, options)return parsed JSON from memory, Cache Storage, or the network; failures and non-OK responses return null
revoke(url)revoke and remove one in-memory media blob URL
delete(url)remove one URL from memory plus the active JSON and media Cache Storage entries
clear()revoke every blob URL, clear pending/memory maps, and delete every ReactWP-managed generation

Both fetch helpers accept an optional requestInit; media defaults to same-origin credentials. They persist only responses whose Cache-Control value is not private or no-store. media() adds the current generation query parameter only to same-origin HTTP(S) requests and returns the original URL when fetching or blob creation fails.

The current loader consumes the media helper. RouteService keeps route payloads in its own session-memory map and does not persist them through the JSON helper. Both mechanisms are separate from the browser's normal HTTP cache. See Cache and Invalidation.

Smooth Scrolling And Shell Boundaries

The frontend runtime also owns the smoother lifecycle through Scroller.js.

The smoother uses:

  • #pageWrapper as the wrapper
  • #pageContent as the transformed content layer

Anything that must stay fixed outside the transformed scroll layer should mount outside that structure.

That is why Header.jsx uses a portal mount instead of staying inside the smoother tree.

See Theme Shell and Scroll.

The exported scroller facade is the project API. Use scroller.init(), kill(), refresh(), scrollTo(), jumpToTop(), lock(), and unlock() rather than assuming window.gscroll always exists.

Motion Helpers

motion.js is the small helper layer shared by the loader and transition runtime.

It centralizes:

  • gsap
  • ScrollTrigger
  • reduced motion detection
  • Promise-based animation completion wiring

This is what allows ReactWP to support both:

  • normal animated flows
  • reduced-motion immediate fallbacks

Available exports are:

ExportBehavior
gsapshared GSAP instance
ScrollTriggershared ScrollTrigger plugin reference
prefersReducedMotionboolean captured when the module initializes; safe during server rendering
once(callback)wrapper that allows one invocation
runAnimation(options)Promise adapter for GSAP animations, Promise-like work, immediate fallbacks, and empty animations

runAnimation() resolves when a GSAP onComplete callback runs, a returned Promise settles, or no animation is returned. It preserves an existing GSAP onComplete callback before adding its own completion wiring.

prefersReducedMotion is a startup snapshot, not a live media-query subscription. A preference changed while the application is already open takes effect after the runtime reloads unless project code implements its own listener.

Low-Level Runtime Exports

Some modules export lower-level values because ReactWP itself, the universal renderer, or tests compose them:

  • RouteContext
  • fetchRoute
  • pageTransition
  • templateRegistry
  • createTemplateEntry
  • resolveTemplateEntry
  • resetTemplateRegistry
  • useRouteTransition
  • useInternalNavigation
  • useDocumentMeta
  • runAnimationLifecycle
  • route helpers normalizePath, normalizeSearch, searchToQuery, queryToSearch, createRouteKey, and normalizeRoute
  • DOM helpers sanitizeDomProps and normalizeHeadingTag
  • server-only render and getTemplateManifest

These are documented here so their role is explicit, but most projects should use the project-facing APIs or configuration files above. Editing the route hook, registry object, raw transition facade, or server renderer changes framework orchestration and should be treated as an intentional runtime fork.

fetchRoute() is the low-level normalized route request and session-memory layer. Normal navigation should go through AppLink, Button, or React Router so loader preparation and transition timing still run.

RouteContext contains the active transition state used by App.jsx; templates already receive the active route as a prop and normally do not need to consume that context directly.

runAnimationLifecycle() is the lower-level Promise adapter used by motion.js and lifecycle tests. It accepts the reduced-motion decision explicitly. Normal project code should use runAnimation(), which supplies ReactWP's runtime preference snapshot.

The route helpers are the canonical normalization functions used by Runtime.js, RouteService.js, and the router bridge. Use them when project code constructs a ReactWP route key or converts between search and query; do not reproduce slightly different slash or query normalization.

sanitizeDomProps() removes framework-only and unsafe values before a component spreads props onto a DOM node. normalizeHeadingTag() accepts only h1 through h6 and otherwise returns its fallback. The shipped components already call these helpers where appropriate.

Head Sync

After a client-side navigation, ReactWP can re-sync document head tags from route.head.

That flow is handled by:

  • src/themes/reactwp/js/inc/useDocumentMeta.js

If route.head is a non-empty array, ReactWP parses at most 100 bounded entries and synchronizes only supported nodes: text-only <title>, safe <meta>, and HTTP(S) <link> elements whose relation is alternate, apple-touch-icon, canonical, icon, or manifest. Scripts, styles, refresh directives, arbitrary nodes, URL credentials, and unsupported link relations are discarded in the browser even if a custom PHP filter emitted them.

When route.head is empty, the smaller fallback uses route.seo, runtime.seoDefaults, and site values to set the document title, description, og:type, og:url, og:title, og:description, and optional og:image. The fallback does not create a canonical <link>; add that through rwp_wp_head when the project needs one in both direct and client navigations.

This matters when you customize rwp_wp_head on the PHP side and expect those tags to survive React navigation.