ForgeWP

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

Table · 2 columns
Model
What editors work with
Hero attributes
Named values inside one hero block
Page BlockArea
The Page's complete saved body at a template location
Parent shell with innerBlocks
Independent 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:

Theme terminal: scaffold a stacked parent shell
pnpm forgewp make:shell ContentSection --layout=stack --yes

Replace src/blocks/ContentSection.tsx with this complete definition:

src/blocks/ContentSection.tsx
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

Table · 2 columns
Setting
Purpose
allowedBlocks
Offers headings, paragraphs, and images as direct children
template
Supplies the initial heading and paragraph for a newly inserted shell
templateLock: false
Allows child insertion, removal, and reordering, subject to other editor settings
orientation: "vertical"
Gives Gutenberg a vertical arrangement hint for child controls
shell.className
Styles the outer section wrapper
shell.gridClassName
Styles 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

Theme terminal: check and export the shell
pnpm typecheck
pnpm forgewp export --validate --strict

Use your project's equivalent typecheck if its scripts differ from the starter.

  1. Update the theme on your WordPress test site.
  2. Open a Page using ForgeWP Builder or a template with a BlockArea body slot.
  3. Insert Content Section. It starts with a heading and a paragraph.
  4. Open Gutenberg's List View to see the parent and its children.
  5. Edit the child text, add an Image block inside the parent, and reorder the children.
  6. 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

Table · 2 columns
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 instance
The inner inserter offers different blocks
Check allowedBlocks, user permissions, and any surrounding template restrictions
Changing 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.