Compiler Contracts and Translation Rules
A complete reference of ForgeWP compilation rules: how React data hooks, JSX primitives, editable schemas, Gutenberg blocks, and forms map to native WordPress PHP, editor scripts, or client runtime islands.
The power of ForgeWP lies in its clear compilation contracts. Rather than trying to emulate a full Node.js runtime inside PHP, ForgeWP translates recognized React hooks, primitives, and component patterns into the appropriate WordPress output.
Every line of code you write in a ForgeWP theme is compiled into one of three execution environments:
- Compile-Time Server (PHP / Static HTML): Evaluated during the export pass and written as native PHP templates with WordPress template tags. Zero client JavaScript is sent to the visitor.
- Editor Environment (Gutenberg in
/wp-admin): Compiled into@wordpress/block-editorcomponents, attribute schemas (block.json), and sidebar controls for content creators. - Client Runtime (Hydrated Browser Islands): Bundled into isolated vanilla JavaScript chunks that execute in the visitor's browser only when triggered.
The Master Translation Matrix
React Construct Primary Execution Host Emitted WordPress Output Client JS Shipped? useWpTitle()Server (PHP) get_the_title() with entity decodingNo (0 KB) useWpContent()Server (PHP) the_content()No (0 KB) useWpExcerpt()Server (PHP) the_excerpt()No (0 KB) useWpPermalink()Server (PHP) the_permalink()No (0 KB) useWpDate()Server (PHP) get_the_date()No (0 KB) useWpAuthor()Server (PHP) get_the_author()No (0 KB) useWpFeaturedImage()Server (PHP) get_the_post_thumbnail_url()No (0 KB) useWpCustomField()Server (PHP) get_post_meta()No (0 KB) useWpMeta()Server (PHP) forgewp_get_meta_value()No (0 KB) useWpOption()Server (PHP) get_option()No (0 KB) useWpThemeMod()Server (PHP) get_theme_mod()No (0 KB) useWpI18n()Server (PHP) __('text', 'domain')No (0 KB) useWpLanguage() (read-only)Server (PHP) Polylang language URLs No (0 KB) <BlockArea />Server (PHP) the_content() in template slotNo (0 KB) <WpMenu />Server (PHP) wp_get_nav_menu_items()No (0 KB) <WpShortcode />Server (PHP) do_shortcode()No (0 KB) <WpQueryLoop> (static)Server (PHP) new WP_Query(...) loopNo (0 KB) defineBlock()Dual Host block.json, render.php, editor JSOnly in Admin <WpEditable />Dual Host Editor: RichText / Frontend: textOnly in Admin useWpQuery()Client Island Hydrated island fetching via REST Yes (isolated chunk) useWpForm()Client Island Native form submitter & validation Yes (isolated chunk) onClick, useStateClient Island Extracted island via <Hydrate>Yes (isolated chunk) |
|---|
WordPress Data Hooks to PHP Mappings
During the compile-time SSR pass, ForgeWP data hooks emit intermediate tokens that the markup processor converts into PHP expressions:
1. Core Post Data
1import { useWpTitle, useWpDate, useWpAuthor } from "@forgewp/react"
2
3export function ArticleHeader() {
4 return (
5 <header>
6 <h1>{useWpTitle()}</h1>
7 <p>Published on {useWpDate()} by {useWpAuthor()}</p>
8 </header>
9 )
10}1<header>
2 <h1><?php echo html_entity_decode( get_the_title(), ENT_QUOTES | ENT_HTML5, 'UTF-8' ); ?></h1>
3 <p>Published on <?php echo esc_html( get_the_date() ); ?> by <?php echo esc_html( get_the_author() ); ?></p>
4</header>2. Custom Meta Fields and Editable Schemas
The useWpCustomField() hook maps directly to WordPress post meta:
1import { useWpCustomField } from "@forgewp/react"
2
3export function ProjectBadge() {
4 const clientName = useWpCustomField("client_name", "Private Client")
5 return <span className="badge">{clientName}</span>
6}1<span class="badge"><?php echo esc_html( get_post_meta( get_the_ID(), 'client_name', true ) ); ?></span>For schema-managed fields using useWpMeta(), the compiler generates calls to the forgewp_get_meta_value() helper:
1<?php echo forgewp_get_meta_value( 'hero_title', 'Welcome', false, null ); ?>This helper:
- Checks for post meta via
get_post_meta()or ACF'sget_field(). - Falls back to the default value declared in your schema if the meta is empty.
- Automatically handles front-page language translations when viewing Polylang sites.
JSX Primitives to WordPress Output
ForgeWP provides specialized JSX components that act as compile-time directives:
1. <BlockArea /> (Designated Post Content Slot)
When authoring page templates or single post layouts, <BlockArea /> designates where WordPress Block Editor content renders:
1import { BlockArea, useWpTitle } from "@forgewp/react"
2
3export function SinglePost() {
4 return (
5 <article className="prose mx-auto py-12">
6 <h1>{useWpTitle()}</h1>
7 <BlockArea name="post-content" />
8 </article>
9 )
10}1<article class="prose mx-auto py-12">
2 <h1><?php echo html_entity_decode( get_the_title(), ENT_QUOTES | ENT_HTML5, 'UTF-8' ); ?></h1>
3 <?php the_content(); ?>
4</article>2. <WpMenu /> (Navigation Walker)
<WpMenu /> emits a server-side navigation loop that resolves the correct menu based on the active language:
1import { WpMenu } from "@forgewp/react"
2
3export function Navbar() {
4 return (
5 <header>
6 <WpMenu location="primary" className="flex gap-4" linkClassName="text-sm font-medium" />
7 </header>
8 )
9}1<header>
2 <?php
3 $lang = function_exists('pll_current_language') ? pll_current_language() : '';
4 $menu_id = function_exists('forgewp_get_menu_id_for_lang') ? forgewp_get_menu_id_for_lang('primary', $lang) : null;
5 $menu_items = $menu_id ? wp_get_nav_menu_items($menu_id) : array();
6 if (!empty($menu_items)) {
7 echo '<nav class="flex gap-4">';
8 foreach ($menu_items as $item) {
9 echo '<a href="' . esc_url($item->url) . '" class="text-sm font-medium">' . esc_html($item->title) . '</a>';
10 }
11 echo '</nav>';
12 }
13 ?>
14</header>3. <WpShortcode /> (WordPress Shortcode Evaluator)
<WpShortcode /> displays an illustrative placeholder in local Vite preview. During export, the compiler converts it to a standard do_shortcode() call:
1<WpShortcode code="[contact-form-7 id='42' title='Inquiry']" />1<?php echo do_shortcode('[contact-form-7 id=\'42\' title=\'Inquiry\']'); ?>Editable Content Schemas and Rich Text Rules
When you declare field schemas with defineEditable(), the compiler enforces strict typing rules for how content is stored and escaped:
1import { defineEditable, text, richText, image } from "@forgewp/react"
2
3export const editable = defineEditable({
4 hero_title: text({ default: "Luxury Mountain Suites" }),
5 hero_description: richText({ default: "<p>Experience handcrafted chalet living.</p>" }),
6 hero_image: image({ default: "/images/hero.jpg" }),
7})Why the Schema is Authoritative for wpautop()
In standard WordPress, rich text fields need wpautop() to convert double line breaks into <p> paragraphs. Plain text fields must never be run through wpautop(), otherwise WordPress will wrap plain strings in <p> tags, breaking layout styling and double-nesting tags.
The ForgeWP compiler tracks the exact set of fields declared as richText(). When compiling useWpMeta("hero_description"), it marks the third parameter of forgewp_get_meta_value() as true:
1<?php echo forgewp_get_meta_value( 'hero_description', '...', true, null ); ?>When compiling hero_title (declared as text()), the parameter is false, ensuring plain text is escaped safely without unintended paragraph wrapping.
Dual-Host Block Transpilation
When you define a Gutenberg block with defineBlock(), the compiler processes your component for two distinct target environments:
Host 1: The Gutenberg Editor (assets/forgewp-editor.js)
The editor transpiler (editor-transpiler.js) transforms your JSX into a WordPress Block Editor component:
- Injects
useBlockProps(). - Converts
<WpEditable name="title">into@wordpress/block-editor's<RichText value={attributes.title} onChange={(val) => setAttributes({ title: val })} />. - Connects custom attributes to the block sidebar inspector.
- Emits
block.jsoncontaining the attribute schemas and block metadata.
Host 2: The Visitor Frontend (render.php)
The PHP transpiler (php-transpiler.js) converts that same JSX into a fast, server-side PHP renderer:
- Strips all
<WpEditable>wrappers. - Replaces attribute values with
$attributes['title'] ?? 'default'. - Transpiles JSX conditional ternaries (
{condition ? <ComponentA /> : <ComponentB />}) into PHPif/elsestatements. - Transpiles JSX array mappings (
{items.map(...)}) into PHPforeachloops.
Visitors receive 100% static, server-rendered HTML with zero client JavaScript overhead for the block.
Selective Hydration Boundaries (is-interactive.js)
ForgeWP's AST analyzer inspects components to identify static markup and interactive islands, helping keep browser JavaScript focused on the parts that need it.
The SERVER_SAFE_HOOKS Allowlist
Hooks on the SERVER_SAFE_HOOKS allowlist do not by themselves require hydration. A component that otherwise stays static can need no component-specific browser JavaScript. Interactive children, imports, surrounding islands, and shared scripts still affect the full page payload:
useWpTitle
useWpContent
useWpExcerpt
useWpPermalink
useWpDate
useWpModifiedDate
useWpAuthor
useWpFeaturedImage
useWpCustomField
useWpMeta
useWpOption
useWpThemeMod
useWpThemeUri
useWpI18n
useWpLanguage (without switchLanguage handlers)
useWpMenu
useReducedMotion
useMemo
useCallback
useIdWhat Triggers Client Hydration?
A component is automatically flagged as interactive when it uses:
- React state or effect hooks:
useState,useEffect,useReducer,useRef. - Asynchronous data fetching hooks:
useWpQuery,useWpForm. - DOM event handlers:
onClick,onChange,onSubmit,onMouseEnter. - Client animation packages:
framer-motion,motion/react,gsap. - Explicit hydration boundaries:
<Hydrate>.
Debugging Compiler Transformations
When an exported page or block does not render as expected, follow this systematic debugging procedure:
1. Inspect Generated PHP Templates
Open the compiled files inside .forgewp/out/<theme-slug>/:
- Inspect
functions.phpto verify enqueues, registered post types, and REST endpoints. - Inspect
page.phporfront-page.phpto verify that your markup tokens were replaced by PHP tags. - Inspect
blocks/<slug>/render.phpto verify how block attributes are read.
2. Run Export in Strict Mode
Run export with --validate --strict to expose unmapped editable fields or missing hydration assets:
pnpm forgewp export --validate --strict3. Verify PHP Syntax Locally
If you have PHP installed on your development machine, verify syntax across all generated files:
php -l .forgewp/out/alpine-resort/functions.php
php -l .forgewp/out/alpine-resort/front-page.phpNext Steps
Now that you understand the compiler contracts, translation rules, and dual-host block architecture, proceed to AST Transformations and Transpiler Internals to explore how Babel and TypeScript syntax trees, static node evaluation, and range-splicing power ForgeWP's compilation engine.