Turn a React Component into a Block
Reuse an existing React section as a Gutenberg block with ForgeWP's block annotation and inline-editable text.
If you already have a React section that should also be insertable from Gutenberg, you do not have to rewrite it as a separate block by hand. ForgeWP can discover a component marked with @forgewp-block and generate a block wrapper during export.
This is one alternative to writing defineBlock() directly. The annotation path is convenient, but it is less expressive: the scanner infers simple attributes and generates the wrapper's edit function. Use defineBlock() when you need a custom editor view, a save view, or parent InnerBlocks. Use Editable Content Choices to decide whether any of the component's content should be editable at all.
Add the block annotation
Place a JSDoc comment immediately above the exported component (or its exported props interface) and add @forgewp-block. The compiler reads the title, category, and icon metadata. It can infer simple block attributes from string, number, and boolean component props.
1import { WpEditable } from "@forgewp/react"
2
3/**
4 * @forgewp-block
5 * title: Notice Section
6 * category: design
7 * icon: info
8 * description: A notice section editors can insert in Gutenberg.
9 */
10export interface NoticeSectionProps {
11 title?: string
12 body?: string
13 setAttributes?: (attributes: Partial<NoticeSectionProps>) => void
14}
15
16export function NoticeSection({
17 title = "A helpful notice",
18 body = "Add details that visitors need to know.",
19 setAttributes,
20}: NoticeSectionProps) {
21 return (
22 <aside className="border border-border bg-card p-6">
23 {setAttributes ? (
24 <WpEditable
25 tagName="h2"
26 value={title}
27 onChange={(value) => setAttributes({ title: value })}
28 className="text-xl font-bold"
29 />
30 ) : (
31 <h2 className="text-xl font-bold">{title}</h2>
32 )}
33 {setAttributes ? (
34 <WpEditable
35 tagName="p"
36 value={body}
37 onChange={(value) => setAttributes({ body: value })}
38 className="mt-2 text-muted-foreground"
39 />
40 ) : (
41 <p className="mt-2 text-muted-foreground">{body}</p>
42 )}
43 </aside>
44 )
45}setAttributes is the signal that the generated wrapper passes to the component. WpEditable becomes an inline RichText control in the Gutenberg editor. Outside that editing context, this component renders ordinary heading and paragraph markup.
With defineBlock(), a save callback can render a named React component declared in the same TSX file or imported from another file. ForgeWP expands that component's supported JSX into the block's visitor-side render.php; the editor callback continues through the separate editor-preview pipeline. Keep browser-only behavior in hydrated components and verify the generated output with pnpm export.
The annotation scanner infers basic attribute types from the props interface. It recognizes string, number, and boolean props, and it can use a shared editable schema when the component uses defineEditable(), pickEditable(), or mergeEditable(). For more control over attribute defaults, labels, inspector controls, repeaters, or the editor/save split, use defineBlock() directly as shown in the previous guide.
Export and find the generated block
Run the theme export:
pnpm exportForgeWP scans TSX source under src/ for the annotation, generates a wrapper under src/blocks/generated/, and compiles the block with the rest of the project's blocks. The exported theme contains a block directory similar to:
blocks/
└── notice-section/
├── block.json
└── render.phpThe block is registered by the generated functions.php and appears in the block inserter with the title Notice Section. Insert it into a post or page, edit the RichText fields, save, and preview the frontend output. The generated wrapper lives under src/blocks/generated/; do not edit it by hand because the scanner can regenerate it.
What if I only want the section on a fixed page?
Leave off @forgewp-block. The React component can still be imported and used in a theme layout; the annotation asks ForgeWP to expose it as a reusable Gutenberg block too.
Is `WpEditable` compulsory?
No. A block can render fixed text, expose values through sidebar controls, contain child blocks with InnerBlocks, or read WordPress fields without using inline editing. Add WpEditable only to the values that should be edited directly in the block canvas.
How should I structure components for block export?
Use supported JSX, attributes, and expressions for the PHP-rendered markup. Put browser-only state, network requests, and interactive helpers in hydrated components, and provide an editor-safe preview where needed. If the editor encounters an unavailable value, it displays a preview notice; use export diagnostics to resolve the binding. Verify both the editor canvas and the visitor-facing output.