Loading...
Loading...
March 24, 2026
When building with a headless CMS like WordPress, there is a specific architectural “uncanny valley” that every developer faces. You fetch content via a clean GraphQL API, but the moment you render that content on the frontend, the Single Page Application (SPA) experience often collapses.
Headless CMS platforms deliver body content as a raw HTML string. Within that string are standard anchor tags (<a>). In a Next.js environment, if I simply pass that string into a container using dangerouslySetInnerHTML, those links remain “dead” to the framework.
Clicking an internal link inside a blog post or page triggers a hard browser refresh. This clears the application state, resets active React context, and forces the browser to re-download the entire JavaScript bundle. Additionally, WordPress often emits absolute URLs that don’t match the frontend’s routing structure, leading to broken navigation or 404 errors when moving between the “web” and “docs” applications in a monorepo.
WPContent PipelineI decided to solve this by creating a centralized WPContent component within my shared @repo/wp-utils package. The objective was to move away from scattered implementations and funnel all CMS content through a single, secure, and SPA-aware pipeline.
Instead of an expensive process of parsing every HTML string into React components on the client—which can be a major performance drain for long-form content—I utilized Event Delegation. I attached a single onClick handler to the parent container that intercepts clicks as they bubble up from the raw HTML.
The logic inside WPContent looks for clicks specifically originating from anchor tags. If the link is internal, I prevent the default browser behavior and hand the URL over to the Next.js router.
Crucially, my link interception logic doesn’t just route; it enforces my project’s trailing-slash normalization standard. This ensures 1:1 parity with the trailingSlash: true configuration in my next.config.mts settings, preventing infinite redirect loops and maintaining strict URL canonicalization across the monorepo.
To address the security risks inherent in rendering raw HTML, I integrated DOMPurify into this pipeline. This ensures that any malicious script injection is stripped out before the content ever reaches the DOM, providing a “Security by Default” architecture.
By centralizing this navigation engine, I’ve achieved a seamless user experience where headless content feels as fast and responsive as native React components. Users can navigate from a WordPress-powered post to a product page without losing their UI state or triggering a jarring reload. For me, this is a cornerstone of professional headless architecture: treating third-party data not as a static string, but as a first-class citizen of the application.