The SPA Bridge is the contract between WordPress and the React frontend. Every page request is served as a full HTML document containing an inline JSON payload — the view name, page data, and SEO metadata — which the React application reads on boot and on each subsequent client-side navigation. Understanding this bridge is essential for building new views or debugging data flow issues.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Ahondev/portfolio-v2/llms.txt
Use this file to discover all available pages before exploring further.
The Data Contract: window.__wp_data__
When WebController::view() handles a request, it writes a JSON object into the HTML response as an inline <script> block. The React SPA reads this on startup.
The three globals written by client.php are:
__wp_view__ string maps directly to a key in routes.ts, telling the SPA which React component to render. The __wp_data__ object contains whatever data the PHP controller passed to $this->view('view_name', $data).
window.__wp_data__ and window.__wp_view__ are declared in the Window interface in wp-sync.ts. window.__wp_seo__ is set directly at runtime by wp-router.tsx after each navigation and by client.php on initial load.Boot Sequence
When the browser loads the page, the SPA initialises throughmain.tsx:
Globals are available
PHP has already written
window.__wp_view__, window.__wp_data__, and window.__wp_seo__ into the HTML before the JavaScript executes.boot() is called
boot() from wp-router.tsx runs on page load. It reads the three globals and caches the current page’s data in an in-memory Map<string, PageData> keyed by the normalised URL path.View is resolved
resolveView() reads window.__wp_view__, looks up the matching lazy-import function in routes.ts, and dynamically imports the React page component.TypeScript Helpers (wp-sync.ts)
The wp-sync.ts module exposes the public API for reading WordPress data inside React components:
wp-sync.ts
Using useWPData in a Component
pages/BlogPost.tsx
SPA Navigation
After the initial boot, all internal link clicks are intercepted and handled client-side without a full page reload.Click Interception
wp-router.tsx
target="_blank"- Origin differs from
location.origin - Has
downloadattribute - Has
rel="external"
The navigate() Function
wp-router.tsx
fetchPage() sends a GET {url}?json=1 request with an X-WP-SPA: 1 header. WordPress responds with pure JSON (no HTML shell):
Prefetching
Onmouseover of any internal link, prefetch() is called to warm the cache before the click:
Browser History
- Forward navigation:
history.pushState({}, "", url) - Replace (no history entry):
history.replaceState({}, "", url)— used whenreplace = true(e.g. onpopstate) - Back/forward:
window.addEventListener("popstate", ...)re-runsnavigate()withforce = trueandreplace = true
URL Normalisation
All URLs are normalised before cache lookups:- Trailing slashes are stripped:
/blog/→/blog - Empty path becomes
/: root URL is always/
The SPA bridge does not use
react-router-dom. Routing is entirely handled by wp-router.tsx through the History API, with React only responsible for rendering the correct component tree.