Project · active

Lumina Insights

An enterprise knowledge hub that bridges headless WordPress with an AI-powered Next.js frontend, built for resilience and performance.

Fri, June 20, 2025
6 min read
View on GitHub
Tech Stack:
Next.js 16 TypeScript Tailwind CSS v4 Headless WordPress WPGraphQL Clerk Auth Google Gemini 2.0 Flash Vercel AI SDK shadcn/ui
LOC 4,900
Languages TypeScript

An enterprise knowledge hub that bridges headless WordPress with an AI-powered Next.js frontend. It turns a traditional CMS into a resilient, intelligent platform with gated content, real-time infrastructure monitoring, and RAG-powered search. Every feature has a fallback. Nothing breaks when the AI goes offline.

What Problem This Solves

Most headless CMS demos stop at rendering blog posts. They do not handle auth, AI integration, preview mode, or graceful degradation. I wanted to build something that felt production-ready: a platform where content editors work in WordPress while visitors get a fast, modern frontend with intelligent features that survive real-world failures.

The gap I wanted to fill was between “proof of concept” and “production resilient.” I needed auth without a separate database, AI summaries that do not 404 when quotas run out, draft previews that work across authenticated boundaries, and a search experience that combines instant keyword matching with deep contextual analysis.

How It Works

The architecture has three layers: the CMS backend, the orchestration layer, and the presentation layer.

CMS Layer: WordPress runs headless with WPGraphQL and ACF Pro. Content editors create Resources, Experts, and Topics using standard Gutenberg blocks. A custom must-use plugin redirects the WordPress Preview button to a secure Next.js draft mode endpoint.

Orchestration Layer: Next.js 16 App Router handles routing, rendering, and data fetching. An Apollo Client singleton connects to WPGraphQL with adjustable caching: 60-second ISR for public pages, zero caching for draft previews. A factory-pattern AI module wraps Google Gemini 2.0 Flash with OpenAI fallback support. Server Actions process form submissions, reading list toggles, and AI chat directly on the server.

Presentation Layer: React 19 components render the UI. A BlockRenderer engine maps WordPress Gutenberg blocks to typed React components, solving hydration mismatches with Tailwind child selectors. The homepage streams live infrastructure status and featured resources via React Suspense. Detail pages render gated content with Clerk auth checks.

Data Flow for a Resource Page:

Static param generation at build time → WPGraphQL fetch for resource data → BlockRenderer parses Gutenberg blocks → AI summary generated server-side with heuristic fallback → Reading list state fetched from Clerk metadata → HTML streamed to the client

Why I Chose Next.js Over Astro

I considered Astro for its zero-JavaScript appeal, but this project needs runtime features: authentication, server actions, AI streaming, and draft preview rendering. Astro’s static-first model cannot handle Clerk middleware, Server Actions, or preview rendering without adding a server adapter. Next.js 16’s App Router gives me SSG and ISR for performance, plus server components and actions for interactivity, all in one platform.

I also evaluated Remix. Remix handles forms and mutations well, but its image optimization and streaming Suspense patterns are less mature than Next.js. The partial prerendering and built-in draft mode in Next.js tipped the decision.

Why Clerk Metadata Instead of a Database for Reading Lists

Storing reading lists in Clerk’s public metadata eliminates a database dependency. The reading list persists across devices, syncs automatically with auth state, and requires zero additional infrastructure. The trade-off is a 64KB metadata limit per user, which is generous for a list of slugs. If the list grows beyond that, I would migrate to a dedicated table. For now, it keeps the architecture lean.

Why a Heuristic Fallback Engine for AI Summaries

AI APIs fail. Quotas exhaust. Latency spikes. Rather than letting the UI break, I built a regex-driven heuristic engine that extracts architectural summaries directly from content HTML. It scores sentences by relevance patterns (challenge, solution, impact) and returns three clipped sentences. The user never sees an error. They see an AI-generated summary when the API is healthy, and a heuristic summary when it is not. Both look identical in the UI.

The same resilience pattern applies to the Assistant chatbot. If the LLM is offline, a keyword-matching concierge scans the WordPress context and recommends relevant resources. The fallback is less conversational, but it is always available.

Why WPGraphQL with Custom Fragments

REST API endpoints for WordPress return more data than needed and require multiple round trips. WPGraphQL lets me define exact field selections with fragments. The ResourceCardFields fragment composes ExpertFields for nested relationships. This means one query fetches a resource, its expert headshots, and its topic tags. No over-fetching. No N+1 problems.

What I Would Do Differently

The BlockRenderer currently handles only headings, paragraphs, and quotes. Any unmapped Gutenberg block falls back to raw HTML injection via dangerouslySetInnerHTML. I should have built a more comprehensive block registry from the start, mapping lists, images, and embeds to typed components.

The AI search fetches all resource content into memory to build the RAG context. At 20 resources this is fine. At 200, it will hit memory limits. I should implement chunked retrieval or a vector database for larger content volumes.

The contact form writes leads to WordPress as a custom post type. This couples the CRM to the CMS. A better approach would be to forward leads to a dedicated service or webhook, keeping WordPress focused on content.

What It Achieved

  • ~4,900 lines of TypeScript across 48 pages
  • Zero client-side data fetching for public pages (SSG/ISR)
  • Sub-100ms keyword search with RAG-powered AI fallback
  • 100% UI uptime for AI features via heuristic fallback engine
  • Cross-device reading lists with no database
  • Real-time infrastructure health dashboard aggregating GitHub and Vercel APIs
  • Draft mode with secure Basic Auth via WordPress Application Passwords

What I Learned

Building a custom block renderer taught me that hydration mismatches between server-parsed HTML and client React are subtle. The fix was not to sanitize HTML but to use Tailwind child selectors ([&_h2]:...) to style injected content without altering its structure. This preserves editor intent while keeping styles consistent.

I also learned that Next.js 16 changed middleware file naming conventions. Upgrading from 15 broke my auth intercepts because middleware.ts now requires a specific export pattern. Renaming to proxy.ts and adjusting the matcher config fixed it, but the silent failure during build was frustrating. I now verify middleware behavior on every major platform upgrade.

Mocking Apollo Client in Server Actions is harder than mocking fetch. The client singleton pattern makes it difficult to inject test doubles. I ended up extracting pure logic (the heuristic engine, Zod validators) into standalone functions that are easy to unit test, while keeping the Apollo-dependent actions for integration tests only.

Next Steps

  • Expand the BlockRenderer to cover all core Gutenberg blocks
  • Replace in-memory RAG with chunked retrieval or a vector store
  • Add ACF static summaries as a priority fallback before AI generation
  • Integrate n8n for Assistant long-term memory and workflow orchestration
  • Decouple lead capture from WordPress to a dedicated CRM webhook