Skip to main content

Head and SEO

Role

ReactWP supports two head-rendering moments:

  • the initial WordPress page render through wp_head
  • client-side navigation through the route payload returned by the REST endpoint

If you want a custom tag to exist in both cases, it must be generated through the rwp_wp_head filter.

The rwp_wp_head Filter

The filter signature is:

add_filter('rwp_wp_head', function($wp_heads, $context = []){
// ...
return $wp_heads;
}, 10, 2);

$wp_heads is an associative array of HTML strings keyed however you want.

Example:

add_filter('rwp_wp_head', function($wp_heads, $context = []){

$wp_heads['potato'] = '<meta name="potato" content="He is a vegetable.">';

return $wp_heads;

}, 10, 2);

Why The Second Parameter Matters

On a direct page load, WordPress is rendering the page normally, so conditional tags like is_front_page() work as expected.

During a React navigation, the head payload is built from the route resolver through the REST endpoint. In that context, conditional tags such as is_front_page() are not reliable enough to drive your logic.

That is why ReactWP also passes a second argument: $context.

$context Shape

ReactWP currently passes these keys:

  • source
  • object
  • route

source

source tells you where the filter is being executed.

Possible values:

  • wp_head
  • route

object

object is the current WordPress object when one is available.

Examples:

  • WP_Post
  • WP_Term
  • WP_User
  • null

route

route is the normalized ReactWP route payload when the filter is executed from the route resolver.

This is the same kind of payload the frontend uses during client-side navigation.

Useful values include:

  • $context['route']['path']
  • $context['route']['template']
  • $context['route']['pageName']
  • $context['route']['is404']
  • $context['route']['seo']

Front Page Example

This is the recommended pattern when a tag must work on both direct loads and React navigation:

add_filter('rwp_wp_head', function($wp_heads, $context = []){

$is_front = false;

if(($context['source'] ?? null) === 'wp_head'){
$is_front = is_front_page();
} elseif(($context['source'] ?? null) === 'route'){
$is_front = (($context['route']['path'] ?? null) === '/');
}

if($is_front){
$wp_heads['potato'] = '<meta name="vegetable" content="he-is">';
}

return $wp_heads;

}, 10, 2);

Initial Render vs React Navigation

ReactWP uses the same filter in two places:

  • the SEO render layer during wp_head
  • the route resolver when building route.head

That means:

  • direct load: the tag is printed by PHP
  • client-side navigation: the tag is sent in the route payload and synced by the frontend runtime

If you only rely on a WordPress conditional without using $context, the tag may work on direct loads but fail after clicking through the React app.

Frontend Sync

The frontend reads route.head and updates the document head after navigation.

The relevant files are:

  • src/themes/reactwp/js/inc/useDocumentMeta.js
  • src/mu-plugins/plugins/reactwp/template/inc/runtime/RouteResolver.php
  • src/plugins/reactwp-seo/template/inc/render.php

If route.head is present, ReactWP syncs those tags directly. If not, it falls back to a smaller SEO sync based on route.seo.