WP Reactor produit le bloc WordPress, le renderer React et le typage TypeScript à partir d'un seul schéma. Un fichier à maintenir au lieu de sept.
{
"slug": "spec-list",
"title": "Spec List",
"attributes": {
"heading": { "ui": "TextInput", "label": "Titre" },
"columns": {
"ui": "Select",
"default": "two",
"options": [
{ "label": "Une colonne", "value": "one" },
{ "label": "Deux colonnes", "value": "two" }
]
},
"items": {
"ui": "ArrayRepeater",
"fields": [
{ "key": "term", "ui": "TextInput" },
{ "key": "description", "ui": "Textarea" }
]
}
}
}Trois étapes. Une seule est manuelle : le renderer, le seul fichier que tu écris.
Un JSON déclare les attributs et leur contrôle éditeur. La clé ui est la seule source de vérité : elle dérive à la fois le type WordPress et le type TypeScript.
{
"slug": "spec-list",
"title": "Spec List",
"attributes": {
"heading": { "ui": "TextInput", "label": "Titre" },
"columns": {
"ui": "Select",
"default": "two",
"options": [
{ "label": "Une colonne", "value": "one" },
{ "label": "Deux colonnes", "value": "two" }
]
},
"items": {
"ui": "ArrayRepeater",
"fields": [
{ "key": "term", "ui": "TextInput" },
{ "key": "description", "ui": "Textarea" }
]
}
}
}Six côté thème, un côté webapp. Le registre de blocs et l'import de l'éditeur sont câblés au passage, sans édition manuelle.
$ pnpm gen:block apps/webapp/src/blocks/schemas/spec-list.json
✓ Generated block: wpreactorapp/spec-list
- theme-wp-reactor-app/src/blocks/spec-list/block.json
- theme-wp-reactor-app/src/blocks/spec-list/edit.tsx
- theme-wp-reactor-app/src/blocks/spec-list/index.tsx
- theme-wp-reactor-app/src/blocks/spec-list/save.tsx
- theme-wp-reactor-app/src/blocks/spec-list/render.php
- theme-wp-reactor-app/src/blocks/spec-list/editor.scss
- apps/webapp/src/blocks/ui/SpecListBlock.tsx
- registry wired (webapp, lazy chunk)
- editor import wired (theme)Le seul fichier laissé en placeholder. Les attributs arrivent déjà normalisés : pas de JSON.parse, pas de conversion de tableau.
export function SpecListBlock({ attributes }: SpecListBlockProps) {
const { heading, columns, items } = attributes ?? {};
return (
<section className="border-b border-border bg-canvas">
<h2 className="font-semibold text-3xl tracking-tight">{heading}</h2>
<dl className={columns === "one" ? "grid" : "grid sm:grid-cols-2"}>
{(items ?? []).map((entry, index) => (
<div key={entry.__wpReactorItemId ?? index}>
<dt className="font-mono text-accent-text">{entry.term}</dt>
<dd className="text-muted">{entry.description}</dd>
</div>
))}
</dl>
</section>
);
}Dans Gutenberg, le bloc est rendu par un iframe qui charge la route /preview de la webapp. Le thème parent et le plugin headless fournis par WP Reactor portent le pont postMessage.
iframe → /preview
<HeroBlock attributes={…} />
[ Éditer ]
même fichier — apps/webapp/src/blocks/ui/HeroBlock.tsx
<HeroBlock attributes={…} />
Chaque valeur de ui produit un contrôle Gutenberg et un type TypeScript. La liste est le contrat : déclarer un type à la main n'est jamais nécessaire.
La commande sans argument est interactive : elle liste les schémas à créer et ceux à régénérer.