Skip to main content

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.

Search engine optimisation in a headless WordPress setup requires a deliberate data pipeline: fields authored in the CMS must flow all the way to <meta> tags rendered in the React SPA. WP SSR Framework handles this end-to-end through SEOServiceProvider, which registers ACF field groups, attaches a scoring column to every SEO-enabled post-type list, provides a centralised SEO Dashboard in WP Admin, and — via WebController — injects the fields into the window.__wp_data__ payload consumed by the frontend.

ACF SEO Fields

SEOServiceProvider::registerSEOFields() creates a “Configuration SEO” field group (menu order 999, so it always appears at the bottom of the edit screen) and attaches it to every WordPress page and to any custom post type registered with has_seo: true.
Field keyLabelTypeWidth
seo_titleTitre SEOText50%
seo_descriptionDescription SEOTextarea (3 rows)50%
seo_keywordsMots-clésText50%
seo_og_imageImage OpenGraphImage (medium preview)50%
seo_authorAuteurText50%
The two-column layout (each field at 50%) keeps the group compact. seo_og_image uses ACF’s Image field type and stores the attachment ID, which the frontend resolves to a full URL at render time.

Enabling SEO on a Custom Post Type

Set has_seo to true when registering your post type class. The provider reads PostType::all() at boot and automatically adds the location rule.
// app/PostTypes/Article.php
class Article extends PostType
{
    public bool $has_seo = true;
    // ...
}

Per-Post SEO Score Column

Every SEO-enabled post-type list table in WP Admin gains a “SEO” column. The column evaluates six fields (seo_title, seo_description, seo_keywords, seo_canonical, seo_og_image, seo_author) and renders a compact score widget:
  • Progress bar — proportional fill showing percentage of filled fields
  • Colour-coded label — green (≥ 80 %), amber (≥ 40 %), or red (< 40 %)
  • Missing field warnings — each absent field is listed below the bar with a ⚠ prefix
$color = '#dc2626'; // red
$label = 'Faible';

if ($percent >= 80) {
    $color = '#16a34a'; $label = 'Bon';
} elseif ($percent >= 40) {
    $color = '#f59e0b'; $label = 'Moyen';
}
This makes it easy to spot under-optimised posts at a glance without opening each one individually.

SEO Dashboard

The SEO Dashboard is accessible at WP Admin → SEO (menu position 3, dashicons-chart-area). It requires manage_options and provides a site-wide view of SEO health.
1

Global SEO score

A wide progress bar and percentage figure show the average score across all published pages and CPT posts. The bar uses the same green/amber/red colour scale as the per-post column.
2

Score by post type

A table breaks down the average SEO score per post_type slug, so you can immediately see which content types need the most attention.
3

Critical warnings

Counts of pages missing the four highest-impact fields are surfaced as a red warning list:
  • pages without seo_description
  • pages without seo_title
  • pages without seo_og_image
  • pages without seo_canonical
If no critical issues exist, a green “Aucun problème critique détecté.” message is shown instead.
4

Missing field counts table

A tabular breakdown of how many pages are missing each SEO field, letting you prioritise bulk-editing efforts.
5

Pages to optimise

Any page or CPT post scoring below 80 % appears in a table with its score, the list of missing fields, and a direct edit link — so editors can jump straight to the WordPress editor to fill in the gaps.

How SEO Data Flows to React

WebController::view() reads the ACF post-meta fields for the currently matched page or post and builds a seo object that is embedded in the $__wp_data__ payload printed into the HTML response.
// For WordPress pages
$seo = [
    'site_title'  => get_bloginfo('name'),
    'page_title'  => get_post_meta($page?->ID ?? 0, 'seo_title', true)       ?? '',
    'description' => get_post_meta($page?->ID ?? 0, 'seo_description', true) ?? '',
    'keywords'    => get_post_meta($page?->ID ?? 0, 'seo_keywords', true)    ?? '',
    'author'      => get_post_meta($page?->ID ?? 0, 'seo_author', true)      ?? '',
    'url'         => $path,
];

// For custom post types (via EloquentCPT properties)
$seo = [
    'site_title'  => get_bloginfo('name'),
    'page_title'  => $post->seo_title       ?? '',
    'description' => $post->seo_description ?? '',
    'keywords'    => $post->seo_keywords    ?? '',
    'author'      => $post->seo_author      ?? '',
    'url'         => wp_make_link_relative(get_permalink($post->id ?? 0)),
];
This seo object is available on window.__wp_data__.seo and is also returned verbatim in JSON navigation responses (?json=1), allowing the React wp-router.tsx to update document.title and all relevant <meta> tags on every client-side navigation without a full page reload.
On the first page load, the seo object is embedded in the server-rendered HTML:
{
  "view": "home",
  "seo": {
    "site_title": "Ahon Studio",
    "page_title": "Développeur freelance React & WordPress",
    "description": "Studio créatif spécialisé en développement web sur-mesure.",
    "keywords": "freelance, react, wordpress, développement web",
    "author": "Ahon",
    "url": "/"
  },
  "data": { ... }
}
The seo_canonical field is tracked in the SEO score column but is not yet populated via ACF (it has no dedicated field in the current field group). The column uses get_post_meta directly, so you can set it programmatically or extend the field group to add a URL field.

SEO Checklist

Static HTML for crawlers

Combine SEO fields with SSG so bots receive a fully-rendered page with correct <meta> tags, not a blank React shell.

Custom Post Types

Learn how to register post types with has_seo: true to unlock the SEO field group and scoring column.

Build docs developers (and LLMs) love