ForgeWP

Practice: Add an Editable CTA and Image

Extend the hero recipe with a saved call-to-action label, destination, image, and alternative text.

Start with Build an Editable Hero Block. Keep its heading and description, then add a call to action and an optional image. A call to action, or CTA, is a link inviting the visitor to do something, such as view rooms or contact the hotel.

Your task

Work in src/blocks/HeroBlock.tsx. Extend the existing block rather than registering a second block. Keep its name hero-block and its existing heading and description attributes so the instance you already saved stays connected.

Table · 4 columns
New attribute
Type and control
Initial value
How editors use it
ctaLabel
string, text
Explore our rooms
Edit the link label directly in the canvas
ctaUrl
string, URL
#rooms
Set a destination through the URL control
cover
object, image
null
Select an image from the Media Library
imageAlt
string, text
An empty string
Supply alternative text for an informative image

Render the image when a cover has been selected. Use a single-column layout on narrow screens and place text and image side by side on wider screens.

The initial destination #rooms points to an element on the same page with id="rooms". For a working test, set the CTA URL to an existing Page on your test site or add that anchor target.

Starter guidance

Add these imports to the recipe's existing import statement:

src/blocks/HeroBlock.tsx: imports for the extension
1import { defineBlock, WpEditable, WpImage } from "@forgewp/react"

Use this attribute as the pattern for a structured editor control, then declare the remaining attributes yourself:

src/blocks/HeroBlock.tsx: inside attributes
1ctaUrl: {
2  type: "string",
3  control: "url",
4  label: "CTA destination",
5  default: "#rooms",
6},

Your implementation needs these connections:

  1. Bind the link's href to ctaUrl in both views.
  2. Use WpEditable with tagName="span" for the label inside the editor's link.
  3. Save label edits through setAttributes.
  4. Give cover an image control and use WpImage to display the selected value.
  5. Connect the image's alt to imageAlt.
  6. Keep editor and visitor layouts aligned.

Acceptance criteria

  • The existing hero retains its saved heading and description after you update the theme.
  • The CTA label can be edited in the canvas and survives saving and reloading.
  • The CTA destination can be edited with the generated URL control.
  • Clicking the CTA in Gutenberg keeps the editor open; test actual navigation on the visitor page.
  • Selecting or replacing an image updates the block, and the selection survives reloading.
  • An unselected cover leaves a readable text-only hero.
  • The visitor's image uses the alternative text you supplied.
  • At a narrow viewport, content stacks without horizontal overflow; at a wider viewport, text and image appear side by side.
  • Two instances can use different labels, destinations, and images.

For an informative image, write alternative text that communicates what matters in the picture. An empty alternative is appropriate when the image is purely decorative.

Hints, if you need them

Hint 1: Save the label

The heading already demonstrates the pattern you need: an attribute supplies value, and onChange writes the new value with setAttributes. Use that same connection for ctaLabel.

Hint 2: Select an image

Use type: "object", control: "image", and default: null for cover. The generated Media Library control supplies the selected image value. Pass it to WpImage as src.

Hint 3: Render optional content

A conditional expression such as attributes.cover && (...) lets the image appear after a selection. Keep the surrounding layout readable before the image is selected.

Hint 4: Check both views

The editor uses edit; the visitor renderer comes from save. Add the image, CTA, and matching classes to both. The editor's label needs onChange; the visitor's label only needs its saved value.

Verify your work

Run the starter's typecheck and export commands:

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

Update the theme on your WordPress test site. Start with the hero instance saved during the recipe, then exercise every acceptance criterion. Inspect blocks/hero-block/block.json for the additional attributes and render.php for their visitor bindings.

Compare with the worked solution

When you are ready, open Solution: Hero CTA and Image. Compare the data bindings and verification results, not just the appearance. Different spacing and colors are welcome.

Extend it further

Add an editable eyebrow line above the heading using the same attribute-to-control pattern. Decide whether editors should change it directly in the canvas or through a sidebar field, then verify that two block instances keep independent values.