Block & Editable Schemas Reference
Comprehensive technical reference for defineBlock(), defineEditable(), field builders, schema composition utilities, and runtime block primitives in ForgeWP.
ForgeWP provides a unified, type-safe schema architecture for authoring WordPress Gutenberg blocks and editable page templates directly in React. Instead of writing custom PHP block registration functions, manual block.json files, and separate Advanced Custom Fields (ACF) definitions, developers declare type-safe schemas using defineBlock() and defineEditable().
During local development with Vite (pnpm dev), these schemas supply mock data with instant Hot Module Replacement (HMR). During production export (pnpm export), the ForgeWP compiler translates these schemas into native WordPress block.json manifests, PHP server render templates, Gutenberg editor Inspector Controls, and ACF field group registrations.
1. defineBlock() Reference
The defineBlock() helper declares a Gutenberg block specification. It provides TypeScript autocomplete and acts as an extraction hook for the compiler.
Function Signature
1export function defineBlock<
2 TAttrs = Record<string, any>,
3 TDef extends BlockDefinition<TAttrs> = BlockDefinition<TAttrs>,
4>(def: TDef): TDefBlockDefinition<TAttrs> Configuration Contract
Property Type Default Description namestringRequired Block slug (e.g. 'call-to-action'). The compiler automatically namespaces this to 'forgewp/call-to-action' in block.json.titlestringRequired Human-readable title displayed in the Gutenberg block inserter. categorystringCompiler default Block category slug: "common", "formatting", "layout", "design", "widgets", "promotional".iconstring"admin-generic"Dashicon slug (e.g. 'megaphone', 'archive') or inline SVG string.descriptionstringundefinedShort summary displayed in the Gutenberg block inspector panel. keywordsstring[][]Search terms for discovering the block in the inserter search bar. attributesRecord<string, BlockAttributeDefinition>{}Attribute schema defining editable block controls and default values. innerBlocksBlockInnerBlocksConfigundefinedConfiguration for parent container blocks that accept nested child blocks. shellBlockShellConfigundefinedLayout wrappers and CSS classes for parent shell containers. editComponentType<{ attributes, setAttributes }>Required React component rendered in the Gutenberg block editor canvas. editorPreviewComponentType<{ attributes, setAttributes }>undefinedOptional fallback or alternate component rendered in the editor preview. editorPreviewMode"fallback" | "override""fallback"Controls whether editorPreview replaces edit ("override") or acts as a canvas fallback ("fallback").saveComponentType<{ attributes }>undefinedStatic HTML save component. When omitted, ForgeWP defaults to dynamic server-side rendering via render.php. |
|---|
Parent Shell & InnerBlocks (innerBlocks, shell)
Parent container blocks (e.g. sections, grids, column wrappers) configure nested blocks via innerBlocks:
1export interface BlockInnerBlocksConfig {
2 /** Array of permitted block names (e.g. ['core/paragraph', 'forgewp/feature-card']) */
3 allowedBlocks?: string[]
4 /** Initial block template rendered on insertion */
5 template?: Array<[string, Record<string, any>?]>
6 /** Lock state: false, 'all' (cannot add/remove/move), or 'insert' (cannot add/remove) */
7 templateLock?: boolean | "all" | "insert"
8 /** Editor layout appender orientation */
9 orientation?: "horizontal" | "vertical"
10}The shell property specifies outer CSS classes applied to the parent container:
1export interface BlockShellConfig {
2 /** Classes applied to the outermost section element */
3 className?: string
4 /** Classes applied to the inner grid wrapper enclosing InnerBlocks */
5 gridClassName?: string
6}Complete defineBlock() Example
1import { defineBlock } from "@forgewp/react"
2
3export default defineBlock({
4 name: "cta-banner",
5 title: "Call to Action Banner",
6 category: "promotional",
7 icon: "megaphone",
8 description:
9 "Prominent conversion banner with heading, button, and customizable accent color.",
10 keywords: ["cta", "banner", "signup", "conversion"],
11 attributes: {
12 heading: {
13 type: "string",
14 control: "text",
15 default: "Transform your WordPress experience",
16 label: "Heading",
17 },
18 buttonText: {
19 type: "string",
20 control: "text",
21 default: "Get Started Free",
22 label: "Button Label",
23 },
24 buttonUrl: {
25 type: "string",
26 control: "url",
27 default: "/signup",
28 label: "Button URL",
29 },
30 accentColor: {
31 type: "string",
32 control: "color",
33 default: "#0ea5e9",
34 label: "Accent Color",
35 },
36 showBadge: {
37 type: "boolean",
38 control: "toggle",
39 default: true,
40 label: "Display Badge",
41 },
42 },
43 edit({ attributes, setAttributes }) {
44 return (
45 <div
46 style={{ borderColor: attributes.accentColor }}
47 className="rounded-2xl border-2 bg-card p-8 text-card-foreground shadow-sm"
48 >
49 {attributes.showBadge && (
50 <span
51 style={{ backgroundColor: attributes.accentColor }}
52 className="rounded-full px-2.5 py-1 text-xs font-semibold tracking-wider text-white uppercase"
53 >
54 Limited Offer
55 </span>
56 )}
57 <h2 className="mt-4 text-2xl font-bold">{attributes.heading}</h2>
58 <a
59 href={attributes.buttonUrl}
60 style={{ backgroundColor: attributes.accentColor }}
61 className="mt-6 inline-block rounded-lg px-6 py-2.5 font-medium text-white"
62 >
63 {attributes.buttonText}
64 </a>
65 </div>
66 )
67 },
68})2. defineEditable() & Field Helper Reference
The defineEditable() function declares a structured content schema for page templates, reusable sections, and block attribute sets. It serves as the single source of truth for field types and default values across both React and WordPress.
Function Signature
1export function defineEditable<T extends EditableSchema>(schema: T): TField Helpers Catalog
All field helpers return an EditableField object containing the underlying data type, inspector control settings, and default value.
text(options)
Plain text input field.
- Gutenberg Inspector:
TextControl - ACF Field Type:
text - Options:
label?: string,default?: string,description?: string,required?: boolean
1import { text } from "@forgewp/react"
2
3const title = text({ default: "Featured Amenities", label: "Section Title" })richText(options)
Inline HTML formatted text editor.
- Gutenberg Inspector:
RichTextcomponent with inline formatting controls (bold, italic, link). - ACF Field Type:
wysiwyg - Options:
label?: string,default?: string,description?: string
image(options)
WordPress media library image attachment picker.
- Gutenberg Inspector:
MediaUploadmodal dialog. - ACF Field Type:
image - Return Value Structure:
{ id: number | string, url: string, alt?: string, width?: number, height?: number } - Options:
label?: string,default?: ImageFieldVal,description?: string
boolean(options)
Boolean toggle switch.
- Gutenberg Inspector:
ToggleControl - ACF Field Type:
true_false - Options:
label?: string,default?: boolean,description?: string
number(options)
Numeric input stepper.
- Gutenberg Inspector:
NumberControl - ACF Field Type:
number - Options:
label?: string,default?: number,min?: number,max?: number,step?: number
color(options)
Color picker supporting theme palette presets and custom HEX codes.
- Gutenberg Inspector:
ColorPalette - ACF Field Type:
color_picker - Options:
label?: string,default?: string,description?: string
url(options)
URL input field with automatic sanitization (esc_url_raw).
- Gutenberg Inspector:
URLInput/TextControl - ACF Field Type:
url - Options:
label?: string,default?: string,description?: string
select(options)
Single-selection dropdown menu.
- Gutenberg Inspector:
SelectControl - ACF Field Type:
select - Options:
label?: string,default?: string,options: Array<{ label: string; value: string } | string>
1import { select } from "@forgewp/react"
2
3const layout = select({
4 label: "Layout Style",
5 default: "grid",
6 options: [
7 { label: "Grid (3 Columns)", value: "grid" },
8 { label: "List (Stacked)", value: "list" },
9 { label: "Carousel", value: "carousel" },
10 ],
11})icon(options)
Icon selector dropdown with Lucide icon preview.
- Gutenberg Inspector: Custom searchable icon picker.
- ACF Field Type:
select - Options:
label?: string,default?: string,provider?: string(defaults to'lucide'),options?: string[](optional icon slug allowlist)
repeater(options)
Repeatable collection of structured child fields.
- Gutenberg Inspector: Repeatable panel with drag-to-reorder controls.
- ACF Field Type:
repeater - Options:
fields: Record<string, EditableField>: Sub-field schema for each row item.mode?: "dynamic" | "fixed":"dynamic"allows editors to add/remove/reorder rows."fixed"locks the row count to the default length.min?: number: Minimum permitted rows in dynamic mode.max?: number: Maximum permitted rows in dynamic mode.default?: T[]: Initial row collection.
1import { repeater, text, icon } from "@forgewp/react"
2
3const features = repeater({
4 label: "Feature List",
5 mode: "dynamic",
6 min: 1,
7 max: 6,
8 fields: {
9 iconName: icon({ default: "check-circle", label: "Icon" }),
10 title: text({ default: "High Performance", label: "Title" }),
11 desc: text({ default: "Engineered for speed", label: "Description" }),
12 },
13 default: [
14 {
15 iconName: "zap",
16 title: "Instant Hydration",
17 desc: "Islands load on interaction",
18 },
19 {
20 iconName: "shield",
21 title: "Strict Sanitization",
22 desc: "Server-side input filtering",
23 },
24 ],
25})3. Schema Composition & Utilities
ForgeWP provides composition utilities to eliminate schema duplication between page templates and Gutenberg blocks.
getEditableDefaults()
Extracts typed default values from a defineEditable schema. This allows you to define defaults once in your schema and reuse them across SSR renderers, React component props, and useWpMeta() fallback values.
1export function getEditableDefaults<T extends EditableSchema>(
2 schema: T
3): InferAttributes<T>1import {
2 defineEditable,
3 getEditableDefaults,
4 text,
5 image,
6} from "@forgewp/react"
7
8export const heroSchema = defineEditable({
9 badge: text({ default: "Welcome to Alpine" }),
10 title: text({ default: "Luxury Mountain Living" }),
11 cover: image({
12 default: { id: 1, url: "/images/hero.jpg", alt: "Mountain Resort" },
13 }),
14})
15
16// Type-safe default values inferred directly from the schema
17export const heroDefaults = getEditableDefaults(heroSchema)
18
19export function HeroSection({ content = heroDefaults }) {
20 return (
21 <section>
22 <span>{content.badge}</span>
23 <h1>{content.title}</h1>
24 </section>
25 )
26}pickEditable()
Extracts a subset of fields from a page schema (with optional key renaming) to construct a block attribute schema.
1export function pickEditable<T extends EditableSchema>(
2 schema: T,
3 map: Partial<Record<string, keyof T & string>> | readonly (keyof T & string)[]
4): EditableSchema1import { pickEditable } from "@forgewp/react"
2import { landingPageSchema } from "../../cms/editables/landing"
3
4// Extract only the fields relevant to this block
5export const featureBlockAttributes = pickEditable(landingPageSchema, {
6 heading: "features_heading",
7 subheading: "features_subheading",
8})mergeEditable()
Combines multiple schemas into one attribute bag. This pattern lets you merge content fields (shared with the page template) with block-only layout controls (padding, alignment, toggles):
1export function mergeEditable(...parts: EditableSchema[]): EditableSchema1import { mergeEditable, pickEditable, select } from "@forgewp/react"
2import { aboutPageSchema } from "../../cms/editables/about"
3
4export const blockAttributes = mergeEditable(
5 pickEditable(aboutPageSchema, ["team_title", "team_members"]),
6 {
7 columns: select({
8 label: "Grid Columns",
9 default: "3",
10 options: [
11 { label: "2 Columns", value: "2" },
12 { label: "3 Columns", value: "3" },
13 { label: "4 Columns", value: "4" },
14 ],
15 }),
16 }
17)buildPageEditable()
Flattens multiple section schemas into a unified page-level schema with automated prefixes and renames:
1export function buildPageEditable(
2 sections: PageEditableSectionSource[]
3): EditableSchema1import { buildPageEditable, defineEditable } from "@forgewp/react"
2import { heroSchema } from "../../src/blocks/HeroSection"
3import { featuresSchema } from "../../src/blocks/FeaturesSection"
4
5export const homePageEditable = defineEditable(
6 buildPageEditable([
7 { schema: heroSchema, prefix: "hero_" },
8 { schema: featuresSchema, prefix: "features_" },
9 ])
10)4. Runtime Components
<WpEditable />
Inline editing primitive with tagName? (default div), value? (default empty string), onChange?(value), and className?, plus the chosen element's supported HTML props. Bind value explicitly; it does not accept a field-name shortcut.
1import { WpEditable } from "@forgewp/react"
2
3type Props = {
4 heading: string
5 setAttributes: (changes: { heading: string }) => void
6}
7
8export function EditableHeading({ heading, setAttributes }: Props) {
9 return (
10 <WpEditable
11 tagName="h2"
12 value={heading}
13 onChange={(value) => setAttributes({ heading: value })}
14 className="text-3xl font-semibold"
15 />
16 )
17}Call this shared component from a block's edit implementation with its attributes and setter. The compiler translates recognized bindings into editor RichText controls and visitor output. For a complete block, follow the editable hero recipe. Use a declared Page schema and useWpMeta when the surrounding component reads Page metadata instead.
<BlockArea />
Declares an insertion point where WordPress content editors can insert Gutenberg blocks.
1import { BlockArea } from "@forgewp/react"
2
3export function PageLayout() {
4 return (
5 <main className="container mx-auto px-4 py-8">
6 {/* Editorial block insertion slot */}
7 <div className="prose max-w-none">
8 <BlockArea name="content" />
9 </div>
10 </main>
11 )
12}- Compile-time Output: Inside full-page templates, compiles into PHP
the_content(). Inside parent container blocks, compiles into Gutenberg<InnerBlocks />(or<?php echo $content; ?>inrender.php). - Client JS: 0 KB.
5. Environment Detection Helpers
ForgeWP provides synchronous environment helpers and corresponding React hooks to detect runtime context:
1import {
2 isEditorPreview,
3 useIsEditorPreview,
4 isDecoupled,
5 useIsDecoupled,
6 isWordPress,
7 useIsWordPress,
8} from "@forgewp/react"Function / Hook Return Type Description isEditorPreview() / useIsEditorPreview()booleanReturns true when component is running inside the Gutenberg block editor iframe canvas (window.forgeWpIsEditorPreview === true).isDecoupled() / useIsDecoupled()booleanReturns true when running in Vite local SPA development mode (import.meta.env.DEV or decoupled frontend).isWordPress() / useIsWordPress()booleanReturns true when running inside the live WordPress PHP theme environment. |
|---|
1import { useIsEditorPreview } from "@forgewp/react"
2
3export function InteractiveBanner() {
4 const inEditor = useIsEditorPreview()
5
6 if (inEditor) {
7 return (
8 <div className="rounded-lg border border-dashed border-primary p-4 text-center text-sm text-muted-foreground">
9 Interactive animation preview is disabled in the editor canvas.
10 </div>
11 )
12 }
13
14 return <ComplexCanvasAnimation />
15}Next Steps
Now that you understand the block and editable schema authoring system, proceed to the wp.config.ts Schema Reference to explore complete configuration options for theme identity, design tokens, post types, localization, and compiler plugins.
Advanced field metadata
Editable fields also inherit description, required, customType, skipRegisterMeta, and postTypes metadata. The generated ACF definition uses customType as a field-type override. A relationship override can use postTypes to constrain the picker and returns post IDs. skipRegisterMeta: true and relationship fields skip ForgeWP's ordinary native metadata registration so another field system can own their storage.
Use these options with the actual field provider and inspect the generated registration. They do not turn an arbitrary custom field type into an automatically supported inline Gutenberg control. For standard controls, use the documented field helpers. The BlockAttributeDefinition schema and editable field schema are distinct: block attributes use WordPress types such as string with a control, while editable helpers use field types such as text. ForgeWP maps recognized editable schemas when generating block wrappers.