DEV Community

Cover image for Headless WordPress Architecture: When and How to Implement It — A Guide by Riad Hasan
Riad Hasan
Riad Hasan

Posted on

Headless WordPress Architecture: When and How to Implement It — A Guide by Riad Hasan

Riad Hasan has implemented headless WordPress solutions for clients ranging from e-commerce stores to publishing platforms. This guide explains when headless architecture makes sense and how to build it correctly.

The term "headless" gets thrown around as a buzzword. Riad Hasan cuts through the hype to explain the real benefits, costs, and implementation strategies.
What is Headless WordPress?

Traditional WordPress renders pages server-side using PHP templates. Headless WordPress decouples the backend (content management) from the frontend (presentation).

Traditional WordPress:

WordPress (PHP) → Theme Templates → HTML → Browser

Headless WordPress:

WordPress (API) → Frontend (React/Next.js) → HTML → Browser

"Headless isn't always better," Riad Hasan cautions. "It's a trade-off. You gain performance and flexibility but lose some WordPress convenience."
Enter fullscreen mode Exit fullscreen mode

When Riad Hasan Recommends Headless
Good Candidates
Scenario Why Headless Works
High-traffic sites Better caching, CDN integration
Complex frontends React/Vue interactivity
Mobile apps Same API serves web and mobile
Multi-platform publishing One backend, multiple frontends
Performance-critical Sub-second page loads
Custom user experiences Full frontend control
Poor Candidates
Scenario Why Stay Traditional
Simple brochure sites Overkill complexity
Content-heavy blogs SEO harder to manage
Budget-constrained projects Higher development cost
Non-technical teams Harder content preview
Plugin-dependent sites Many plugins won't work

"I've turned down headless projects because the client didn't need it," Riad Hasan says. "Honesty builds long-term relationships."
Enter fullscreen mode Exit fullscreen mode

Riad Hasan's Headless Architecture
The Stack
Component Technology Purpose
CMS WordPress Content management
API REST API / GraphQL Data delivery
Frontend Next.js Server-side rendering
Styling Tailwind CSS Utility-first styling
Hosting Vercel / Custom Edge deployment
Cache Redis / CDN Performance layer
Implementation: Step by Step
Step 1: WordPress Setup

Riad Hasan configures WordPress as a pure content API.

Install required plugins:

WPGraphQL (or use REST API)
Advanced Custom Fields PRO
Custom Post Type UI
JWT Authentication (if needed)
Enter fullscreen mode Exit fullscreen mode

Disable unnecessary features:

// wp-config.php or functions.php
// Disable WordPress frontend
add_action('template_redirect', function() {
if (!is_admin() && !defined('REST_REQUEST')) {
wp_redirect(home_url('/app/'));
exit;
}
});

// Remove unnecessary WordPress features
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wp_shortlink_wp_head');

Step 2: Custom Post Types and Fields

Riad Hasan structures content with custom post types:

// Register Project post type
function riad_register_project_cpt() {
register_post_type('project', [
'labels' => [
'name' => 'Projects',
'singular_name' => 'Project',
],
'public' => true,
'show_in_rest' => true,
'supports' => ['title', 'editor', 'thumbnail', 'excerpt'],
'has_archive' => true,
]);
}
add_action('init', 'riad_register_project_cpt');

ACF Fields for Projects:

// functions.php
if(function_exists('acf_add_local_field_group')) {
acf_add_local_field_group([
'key' => 'project_fields',
'title' => 'Project Details',
'fields' => [
[
'key' => 'project_url',
'label' => 'Project URL',
'name' => 'url',
'type' => 'url',
],
[
'key' => 'project_stack',
'label' => 'Technology Stack',
'name' => 'stack',
'type' => 'checkbox',
'choices' => [
'laravel' => 'Laravel',
'react' => 'React',
'wordpress' => 'WordPress',
'woocommerce' => 'WooCommerce',
],
],
[
'key' => 'project_year',
'label' => 'Year',
'name' => 'year',
'type' => 'number',
],
],
'location' => [
[
[
'param' => 'post_type',
'operator' => '==',
'value' => 'project',
],
],
],
]);
}

Step 3: API Endpoints

Using REST API:

// Custom REST endpoint
add_action('rest_api_init', function () {
register_rest_route('riad/v1', '/projects', [
'methods' => 'GET',
'callback' => 'riad_get_projects',
'permission_callback' => '__return_true',
]);
});

function riad_get_projects($request) {
$args = [
'post_type' => 'project',
'posts_per_page' => 10,
'orderby' => 'date',
'order' => 'DESC',
];

$query = new WP_Query($args);
$projects = [];

foreach ($query->posts as $post) {
    $projects[] = [
        'id' => $post->ID,
        'title' => $post->post_title,
        'slug' => $post->post_name,
        'excerpt' => $post->post_excerpt,
        'content' => $post->post_content,
        'featured_image' => get_the_post_thumbnail_url($post->ID, 'large'),
        'url' => get_field('url', $post->ID),
        'stack' => get_field('stack', $post->ID),
        'year' => get_field('year', $post->ID),
    ];
}

return rest_ensure_response($projects);
Enter fullscreen mode Exit fullscreen mode

}

Using WPGraphQL (Riad Hasan's preference):

// Custom GraphQL field
add_action('graphql_register_types', function() {
register_graphql_field('Project', 'technologyStack', [
'type' => ['list_of' => 'String'],
'description' => 'Technology stack used',
'resolve' => function($post) {
return get_field('stack', $post->ID);
}
]);
});

Step 4: Next.js Frontend

Riad Hasan's Next.js setup:

// lib/api.js
const API_URL = process.env.NEXT_PUBLIC_WP_API;

export async function getProjects() {
const res = await fetch(${API_URL}/wp-json/riad/v1/projects, {
next: { revalidate: 3600 } // Cache for 1 hour
});

if (!res.ok) {
throw new Error('Failed to fetch projects');
}

return res.json();
}

export async function getProject(slug) {
const res = await fetch(${API_URL}/wp-json/wp/v2/project?slug=${slug}, {
next: { revalidate: 600 }
});

if (!res.ok) {
throw new Error('Failed to fetch project');
}

const data = await res.json();
return data[0];
}

Project listing page:

// app/projects/page.js
import { getProjects } from '@/lib/api';
import Link from 'next/link';

export default async function ProjectsPage() {
const projects = await getProjects();

return (


Projects by Riad Hasan



{projects.map((project) => (

{project.featured_image && (
src={project.featured_image}
alt={project.title}
loading="lazy"
/>
)}

{project.title}


{project.excerpt}



{project.stack?.map((tech) => (
{tech}
))}


View Project →


))}


);
}

Individual project page:

// app/projects/[slug]/page.js
import { getProject } from '@/lib/api';
import { notFound } from 'next/navigation';

export async function generateMetadata({ params }) {
const project = await getProject(params.slug);

if (!project) return {};

return {
title: ${project.title.rendered} | Riad Hasan,
description: project.excerpt,
};
}

export default async function ProjectPage({ params }) {
const project = await getProject(params.slug);

if (!project) {
notFound();
}

return (

{project.title.rendered}


className="content"
dangerouslySetInnerHTML={{ __html: project.content.rendered }}
/>

);
}

SEO Considerations

Headless WordPress requires extra SEO work. Riad Hasan's approach:

  1. Server-Side Rendering

Use Next.js SSR or SSG for SEO-friendly pages:

// Generate static pages at build time
export async function generateStaticParams() {
const res = await fetch(${API_URL}/wp-json/wp/v2/project);
const projects = await res.json();

return projects.map((project) => ({
slug: project.slug,
}));
}

  1. Meta Tag Management

// app/layout.js
export const metadata = {
title: {
template: '%s | Riad Hasan',
default: 'Riad Hasan — Full Stack Developer',
},
description: 'Full Stack Developer specializing in Laravel, React, WordPress',
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://riadhasan.io',
siteName: 'Riad Hasan',
},
};

  1. XML Sitemap

// app/sitemap.js
import { getProjects } from '@/lib/api';

export default async function sitemap() {
const projects = await getProjects();

const projectUrls = projects.map((project) => ({
url: https://riadhasan.io/projects/${project.slug},
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
}));

return [
{
url: 'https://riadhasan.io',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1.0,
},
...projectUrls,
];
}

Performance Results

Riad Hasan's headless implementations consistently achieve:
Metric Traditional WP Headless WP
First Contentful Paint 2.1s 0.8s
Largest Contentful Paint 3.5s 1.2s
Time to Interactive 4.2s 1.5s
Total Blocking Time 380ms 45ms
Cumulative Layout Shift 0.15 0.02
Common Pitfalls Riad Hasan Avoids
Mistake Consequence Solution
No preview functionality Editors can't see changes Build preview mode
Ignoring SEO Lost search traffic SSR + meta management
Over-fetching API data Slow page loads Query optimization
No caching API overload Redis + CDN
Complex authentication Security issues JWT + refresh tokens
Ignoring images Poor performance Next.js Image component
When Riad Hasan Says No to Headless

"I recently advised a client against headless for their blog," Riad Hasan shares. "They had 2,000 articles, no dev team, and needed to publish daily. Traditional WordPress with a fast theme was the right choice."

Signs headless is wrong for you:

Budget under $10,000
No developer for maintenance
Heavy plugin usage
Content team needs visual editing
SEO is primary concern and team lacks expertise

Work with Riad Hasan

Riad Hasan specializes in headless WordPress architecture for:

E-commerce platforms
Publishing sites
SaaS applications
Multi-platform content delivery
High-traffic websites

Services:

Architecture consultation
Headless implementation
Performance optimization
Team training
Ongoing support

Connect with Riad Hasan:

Portfolio: riadhasan.io
Projects: riadhasan.io/projects
LinkedIn: linkedin.com/in/riad-hasan-100a231a6
GitHub: github.com/RiadHasan15
Email: hire.riadhasan@gmail.com

Are you considering headless WordPress for your project? Share your use case in the comments.

wordpress #headless #nextjs #react #webdev #architecture #webdevelopment #javascript

Top comments (0)