Build a Section with Nested Blocks
Create a reusable layout shell that editors fill with native heading, paragraph, and image blocks.
Build a Content Section that editors can insert, fill with smaller blocks, and move as one section. The theme owns the outer layout; Gutenberg owns the child content and its order.
This is a parent shell: a layout wrapper with an InnerBlocks editing region. It complements the fixed Page content slot and the hero with editable attributes.
Choose the content model
Model What editors work with Hero attributes Named values inside one hero block Page BlockAreaThe Page's complete saved body at a template location Parent shell with innerBlocksIndependent child blocks nested inside one reusable section |
|---|
For example, an editor can move a paragraph above an image inside the section, then move the whole section elsewhere in the Page. The shell's classes keep the shared spacing and background consistent.
1. Create the shell
Use a React-adapter theme and a WordPress test site. From the theme root, run:
pnpm forgewp make:shell ContentSection --layout=stack --yesReplace src/blocks/ContentSection.tsx with this complete definition:
1import { defineBlock } from "@forgewp/react"
2
3export default defineBlock({
4 name: "content-section",
5 title: "Content Section",
6 category: "design",
7 icon: "layout",
8 innerBlocks: {
9 allowedBlocks: ["core/heading", "core/paragraph", "core/image"],
10 template: [
11 ["core/heading", { level: 2, content: "A section of your own" }],
12 ["core/paragraph", { content: "Tell readers what makes your stay special." }],
13 ],
14 templateLock: false,
15 orientation: "vertical",
16 },
17 shell: {
18 className: "mx-auto max-w-5xl border border-slate-200 bg-slate-50 px-6 py-10 text-slate-900",
19 gridClassName: "grid gap-6",
20 },
21 edit: () => null,
22})ForgeWP generates the shell's editor UI from innerBlocks and its layout from shell, so this definition uses edit: () => null. The generated visitor renderer wraps the rendered child HTML; a separate save layout is unnecessary for this shell path.
2. Understand the configuration
Setting Purpose allowedBlocksOffers headings, paragraphs, and images as direct children templateSupplies the initial heading and paragraph for a newly inserted shell templateLock: falseAllows child insertion, removal, and reordering, subject to other editor settings orientation: "vertical"Gives Gutenberg a vertical arrangement hint for child controls shell.classNameStyles the outer section wrapper shell.gridClassNameStyles the region containing the child blocks |
|---|
Use full names such as core/image for WordPress blocks. A ForgeWP block can also be allowed by its registered name, such as forgewp/hero-block, when that block is included in the theme.
The starter template is applied when the editor creates a new block instance. Each instance then saves its own child tree. Treat later template changes as new-instance defaults; review existing content separately.
3. Export and insert it
pnpm typecheck
pnpm forgewp export --validate --strictUse your project's equivalent typecheck if its scripts differ from the starter.
- Update the theme on your WordPress test site.
- Open a Page using ForgeWP Builder or a template with a
BlockAreabody slot. - Insert Content Section. It starts with a heading and a paragraph.
- Open Gutenberg's List View to see the parent and its children.
- Edit the child text, add an Image block inside the parent, and reorder the children.
- Save, reload the editor, and view the visitor page.
Use the parent's inner inserter when adding a child. A block inserted alongside the parent belongs to the Page body rather than the nested section.
4. Verify the result
- New sections start with the two configured children.
- The inner inserter offers the three allowed types.
- Editors can change child content and reorder or remove children.
- The visitor sees those children inside the shell's border, background, and spacing.
- A second section saves its content independently.
- Moving the parent moves its children together, where the Page permits parent movement.
- Child images fit the section on a narrow screen.
In the export, inspect blocks/content-section/render.php. It wraps the child HTML supplied by WordPress as $content. The innerBlocks settings travel through the theme's generated editor configuration; inspect the editor configuration rather than expecting those compiler-specific fields in block.json.
This example restricts the parent's direct children. Core blocks remain available elsewhere according to the site's normal editor configuration. For a nested container that itself accepts children, define that container's own rules too.
Common checks
What you see What to check An image appears outside the border Use List View to move it inside Content Section A new section has unexpected starting content Check innerBlocks.template in the installed export and insert a fresh instanceThe inner inserter offers different blocks Check allowedBlocks, user permissions, and any surrounding template restrictionsChanging defaults leaves an old section unchanged Its children are saved content; use a new instance to test new defaults |
|---|
Continue to Practice: Control Child Blocks and Movement to configure a more structured section. See Block and Editable Schemas for the configuration types.