12

12

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!
After building dozens of headless WordPress sites for clients, we kept running into the same problems. Every project started from scratch. Every developer had to figure out ISR strategies, image optimization, and WordPress GraphQL quirks all over again.
We decided to build FlatWP to solve this once and for all.
Most WordPress headless starters are either too basic (just fetch and display) or too opinionated (locked into specific frameworks or hosting). Agencies need something that’s production-ready but flexible enough to customize for different clients.
We wanted a starter that understood real WordPress workflows – ACF, custom fields, WooCommerce, forms – not just blog posts.
FlatWP is built with performance as the foundation. Every architectural decision prioritizes speed:
But the real differentiator is the WordPress plugin. Instead of just showing you how to query WordPress, we built tooling that makes the entire workflow seamless.
This isn’t a demo or proof-of-concept. FlatWP is designed for production use from day one. We include preview mode for editors, form handling, SEO metadata, and all the unglamorous features that matter when shipping to clients.
Our goal is simple: let developers focus on building great experiences, not wrestling with infrastructure.
We’re launching the open-source version this month, with Pro features coming in early 2025. Stay tuned.
Incremental Static Regeneration (ISR) is the killer feature that makes Next.js perfect for headless WordPress. But understanding when and how to use it can be tricky.
ISR lets you update static pages after build time without rebuilding your entire site. You get the speed of static generation with the freshness of server-side rendering.
Here’s how it works: Next.js generates static HTML at build time. After deployment, when someone requests a page, they get the cached version. In the background, Next.js regenerates the page and updates the cache.
Next.js offers two ISR strategies:
Time-based revalidation regenerates pages after a specified interval:
export const revalidate = 3600; // 1 hour
This is great for content that updates predictably, like archive pages or dashboards.
On-demand revalidation regenerates pages when triggered by an API call. This is what FlatWP uses. When you save a post in WordPress, our plugin immediately triggers revalidation:
await fetch('/api/revalidate', {
method: 'POST',
body: JSON.stringify({ paths: ['/blog/my-post'] })
});
We use different strategies for different content types:
This gives you instant updates where they matter, without sacrificing performance.
With ISR, first-time visitors get sub-100ms page loads. The page is pre-rendered, served from the edge, and cached globally. Subsequent visitors get even faster loads from CDN cache.
Compare this to server-side rendering, which queries WordPress on every request. ISR gives you the best of both worlds.
Advanced Custom Fields (ACF) is the go-to solution for flexible WordPress content. But in a TypeScript headless setup, losing type safety on your custom fields is a major pain point.
FlatWP solves this with automatic TypeScript generation from your ACF field groups.
When you query ACF fields through GraphQL, you get untyped data:
const hero = page.acf.hero; // any type - no autocomplete, no safety
This means runtime errors, no IDE support, and constant trips to the WordPress admin to check field names.
Our WordPress plugin exposes ACF schemas as structured JSON. Our codegen tool transforms these into TypeScript interfaces:
interface HeroBlock {
heading: string;
subheading: string;
image: {
url: string;
alt: string;
};
ctaText: string;
ctaUrl: string;
}
Now your components are fully typed:
export function HeroBlock({ fields }: { fields: HeroBlock }) {
return (
<section>
<h1>{fields.heading}</h1>
<p>{fields.subheading}</p>
{/* TypeScript knows exactly what fields exist */}
</section>
);
}
ACF’s Flexible Content field type is perfect for page builders. FlatWP provides a block renderer pattern:
const blockComponents = {
hero: HeroBlock,
features: FeaturesBlock,
testimonial: TestimonialBlock,
};
export function BlockRenderer({ blocks }: { blocks: ACFBlock[] }) {
return blocks.map((block) => {
const Component = blockComponents[block.layout];
return <Component key={block.id} fields={block.fields} />;
});
}
The Pro version will include a library of 20+ pre-built ACF blocks with matching Shadcn components. Hero sections, feature grids, testimonials, pricing tables – all typed, styled, and ready to use.
You’ll be able to build complex page layouts in WordPress while maintaining full TypeScript safety in your React components.
One question we get constantly: “Will my favorite WordPress plugins work with FlatWP?”
The short answer is: most of them, yes. But it depends on what the plugin does.
Content & SEO Plugins:
Media & Assets:
Forms:
Some plugins require custom integration but are totally doable:
WooCommerce: FlatWP Pro will include a headless storefront. You can also use WPGraphQL for WooCommerce for custom implementations.
Multilingual (WPML/Polylang): Works with proper GraphQL queries. FlatWP Pro includes multi-language routing.
Membership plugins: Require custom authentication flow but compatible.
Anything that depends on WordPress themes or relies on server-side rendering:
We’re actively testing and documenting plugin compatibility. Current priorities:
If there’s a plugin you need, let us know. We’re building adapters based on community demand.
WordPress is notorious for bloated images. Editors upload 5MB photos from their phone, and suddenly your site loads like it’s 2010.
In headless WordPress, you have a unique opportunity to fix this at the framework level.
Traditional WordPress generates multiple image sizes on upload. But these sizes are often wrong for modern responsive design. You end up with dozens of unused files cluttering your media library.
Plus, WordPress doesn’t handle modern formats (WebP, AVIF) or responsive srcsets particularly well.
Next.js Image component handles optimization automatically:
But you need to configure it correctly for WordPress.
We create a custom image loader that:
The result? A 5MB WordPress upload becomes a 50KB WebP delivered in milliseconds.
In your next.config.js:
images: {
domains: ['cms.flatwp.com/'],
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
}
Then use it in components:
<Image
src={post.featuredImage.url}
alt={post.featuredImage.alt}
width={1200}
height={630}
priority={isAboveFold}
/>
Even better: prevent bloated uploads in the first place. Our WordPress plugin includes:
We measured a typical blog post with 5 images:
That’s a 92% reduction in bandwidth and 5x faster load times. For free.
One of the biggest challenges with headless WordPress is preview functionality. Editors want to see their drafts before publishing, but your static site only shows published content.
FlatWP’s preview mode solves this elegantly.
When an editor clicks “Preview” in WordPress, our plugin generates a special URL:
https://flatwp.com/api/preview?secret=xyz&id=123&type=post
This hits your Next.js preview API route, which:
Now when Next.js renders the page, it queries WordPress for draft content instead of published content.
Your preview API route:
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const secret = searchParams.get('secret');
const id = searchParams.get('id');
// Verify secret
if (secret !== process.env.PREVIEW_SECRET) {
return new Response('Invalid token', { status: 401 });
}
// Enable draft mode
draftMode().enable();
// Redirect to the post
redirect(`/blog/${slug}`);
}
Then in your page component:
export default async function Post({ params }) {
const { isEnabled } = draftMode();
// Fetch draft if preview mode is enabled
const post = await fetchPost(params.slug, {
preview: isEnabled
});
return <PostTemplate post={post} />;
}
Our plugin adds a “Preview on Frontend” button to the WordPress editor that generates the preview URL automatically.
It also handles authentication – only logged-in WordPress users can generate preview links, keeping your drafts secure.
We add a banner to preview pages:
{draftMode().isEnabled && (
<div className="bg-yellow-100 p-4">
<p>You are viewing a preview.</p>
<a href="/api/exit-preview">Exit Preview</a>
</div>
)}
The exit route simply clears the draft mode cookie.
Without preview mode, editors have to publish content to see how it looks. This breaks their workflow and risks publishing unfinished work.
With FlatWP’s preview mode, editors can iterate on drafts, share preview links with teammates, and only publish when ready.
It’s a small feature that makes a huge difference in adoption.
WordPress offers both REST API and GraphQL for headless implementations. We deliberately chose GraphQL for FlatWP, and here’s why.
WordPress REST API returns everything about a post, whether you need it or not:
GET /wp-json/wp/v2/posts/123
You get the author object, meta fields, embedded media, comment stats, and dozens of other fields you’ll never use. This bloats response sizes and slows down your site.
With GraphQL, you request exactly what you need:
query GetPost($id: ID!) {
post(id: $id) {
title
content
featuredImage {
url
alt
}
}
}
The response contains only those fields. Nothing more, nothing less.
GraphQL’s typed schema enables automatic TypeScript generation. Our codegen process creates perfect types from your queries:
// Auto-generated from GraphQL schema
interface GetPostQuery {
post: {
title: string;
content: string;
featuredImage: {
url: string;
alt: string;
};
};
}
This is nearly impossible with REST API without manually maintaining types.
REST API requires multiple requests for nested data:
// Get post
GET /wp-json/wp/v2/posts/123
// Get author
GET /wp-json/wp/v2/users/5
// Get categories
GET /wp-json/wp/v2/categories?post=123
GraphQL fetches everything in one query:
query GetPost($id: ID!) {
post(id: $id) {
title
author {
name
avatar
}
categories {
name
slug
}
}
}
Fewer requests = faster page loads. We measured:
WPGraphQL is mature, well-maintained, and has a huge ecosystem:
Popular plugins have GraphQL extensions, making integration seamless.
GraphQL isn’t always the answer. Use REST API when:
But for full-featured headless sites, GraphQL’s benefits are undeniable.
FlatWP works on any platform that supports Next.js, but the deployment choice significantly impacts performance and developer experience.
Vercel created Next.js, so integration is seamless:
Pros:
Cons:
Best for: Most projects, especially if you value DX and don’t mind the cost at scale.
Netlify has excellent Next.js support via their Essential Next.js plugin:
Pros:
Cons:
Best for: Teams already on Netlify or those wanting to save costs.
Cloudflare now supports Next.js via their @cloudflare/next-on-pages adapter:
Pros:
Cons:
Best for: High-traffic sites on a budget.
You can deploy Next.js to any Node.js server:
Pros:
Cons:
Best for: Enterprise with existing infrastructure or strict data requirements.
Start with Vercel. The DX is unmatched and the free tier is generous enough for most projects. Once you’re at scale and costs matter, evaluate Netlify or Cloudflare.
For the WordPress backend, use any WordPress host – FlatWP doesn’t care where WordPress lives as long as GraphQL is accessible.