Practice: Build a Featured Story Section
Use a category-filtered query to show one featured story, including a readable fallback for posts without images.
Start with Build a Latest Posts Block. Build a second block named Featured Story that displays the newest published Post in the featured category.
In this exercise, featured is a category you create and assign. That makes curation a standard Post-editing task. WordPress's sticky-post setting and a custom field named featured are separate ways of marking content; this exercise uses the category slug.
1. Prepare the content
On your WordPress test site:
- Create a category named Featured with the slug
featured. - Assign two published Posts to it and give them different publication dates.
- Keep another published Post outside that category.
- Leave the newest matching Post without a featured image for the first test.
The query should display the newest matching Post, even when a newer Post exists outside the category.
2. Build the block
pnpm forgewp make:block FeaturedStory --attributes=headingWork in src/blocks/FeaturedStory.tsx. Keep the listing block from the recipe so both can be inserted into the same Page.
Your block should have:
- Registered name
featured-storyand inserter title Featured Story. - An inline-editable section heading, initially Featured story.
- A
WpQueryLoopusingpostType="post",categoryName="featured", andpostsPerPage={1}. - Explicit newest-first date ordering.
- A child card that reads the title, excerpt, permalink, and featured image from the current Post.
- The recipe's placeholder SVG when the Post has no featured image.
- A stacked layout on narrow screens and a side-by-side image/text layout on wider screens.
Acceptance criteria
Test Expected result Two published Posts match the category Exactly one card: the newest matching Post A newer Post is outside the category It stays outside the featured section The chosen Post has no image A styled placeholder appears; title, excerpt, and link remain usable You add a featured image The visitor page displays that image after saving and cache refresh You change the section heading The new heading survives an editor reload You change the Post's title The card follows the Post data while the block heading stays independent You remove all matching Posts from the category The example's query area is empty; choose a deliberate empty-state design for production You insert the listing and featured blocks together Each query uses its own result context |
|---|
Test the live result on a Page using ForgeWP Builder or a template with a saved-content slot. The query's selection is verified against published WordPress data.
Hints
Hint 1: Filter by slug
Use categoryName="featured". The slug, rather than the category's display label, is the query input. Adding this category to WordPress is a content step; adding the query prop selects it.
Hint 2: Supply an image fallback
Use useWpFeaturedImage() || "/assets/story-placeholder.svg" as the image source. This calls the hook once and uses the theme asset when the returned URL is empty. Keep the fallback file created in the recipe.
Hint 3: Separate the headings
The section heading is a saved block attribute, while the story title comes from useWpTitle() inside the query. Give the story title a linked heading under the section heading.
Hint 4: Share the card layout
A card component in the same file can be used in both edit and save. Keep the card beneath WpQueryLoop in each view so it receives the queried Post's context.
Verify and compare
pnpm typecheck
pnpm forgewp export --validate --strictUpdate the test theme, insert Featured Story, and run through the acceptance criteria. Inspect blocks/featured-story/render.php for the one-post limit, category filter, date ordering, and post-context reset.
Then open Solution: Featured Story Section to compare your bindings and query settings.
Extension
Change the category slug to curate a second section, such as travel-tips. Create matching Posts in WordPress and verify that each block uses the intended category. Keep this version's query choices in source; configurable query controls can be a separate iteration.