ItemList carousel schema: summary pages and all-in-one lists
ItemList schema turns a list page into a set of cards that searchers can swipe through on mobile, all from your site. Google calls this a host carousel. The list itself is simple: an ItemList whose itemListElement holds two or more ListItem entries, each with a position.
The catch is what can be listed. Host carousels support four content types only: courses, movies, recipes and restaurants. A "10 best hiking backpacks" post can use ItemList as valid schema.org, but it won't produce a carousel. Each listed item also needs its own complete markup, so the list only works once the Recipe, Movie or course pages behind it are valid.
JSON-LD example
Copy this into a <script type="application/ld+json"> tag and replace the values with your own.
{
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"url": "https://example.com/recipes/brown-butter-chocolate-chip-cookies/"
},
{
"@type": "ListItem",
"position": 2,
"url": "https://example.com/recipes/chewy-oatmeal-raisin-cookies/"
},
{
"@type": "ListItem",
"position": 3,
"url": "https://example.com/recipes/lemon-crinkle-cookies/"
}
]
}
Properties
| Property | Status | What it is |
|---|---|---|
itemListElement | Required | Two or more ListItem entries, all describing the same type. Don't mix recipes and restaurants in one list. |
ListItem.position | Required | The item's place in the list, starting at 1. Google shows items in this order. |
ListItem.url | Required | Summary pages only: the canonical URL of the item's own page. Each URL must be unique and on the same domain, or a subdomain or parent domain of it. |
ListItem.item | Required | All-in-one pages only: the full object, with name, url and the properties its type requires. |
item.url | Recommended | On an all-in-one page, an anchor on the same page, such as #lemon-bars. |
item.name | Recommended | The item's name as shown on the page. |
Summary page or all-in-one page?
Google supports two layouts, and the ListItem properties change between them.
| Layout | What the page contains | What each ListItem holds |
|---|---|---|
| Summary page | A short blurb per item, each linking to a separate detail page | Only @type, position and url |
| All-in-one page | The full content of every item on one page | position and a complete item with name, url (an anchor) and type properties |
A roundup like "12 cookie recipes for a bake sale" that links to twelve recipe posts is a summary page. A single post that contains all twelve full recipes is an all-in-one page. Don't combine the two approaches in one list.
Rules that break most list markup
- Every item must be the same type. A list of recipes with one restaurant mixed in fails.
- The list must be complete. If the page shows 12 items, the markup lists 12, not the first five.
- Markup must match the page. Visible names and order should agree with the structured data.
- Each detail page needs valid markup of its own. For summary pages, Google asks you to test every linked URL, because the carousel cards come from those pages.
- URLs must be unique and stay on your domain. Two ListItems can't point to the same page.
Also know that some regions, including the EEA, Turkey and South Africa, have a separate beta called structured data carousels with its own rules and interest forms. This page covers the host carousel described in Google's main documentation.
Building the list from a WordPress roundup
Recipe roundups are the most common WordPress use. The linked recipe posts usually get their Recipe markup from a recipe card plugin, so the roundup only needs the summary-page list. Store the linked post IDs in a custom field, in display order, and generate the ListItems from them:
add_filter( 'hydrogen_seo_schemas', function ( $schemas ) {
$ids = get_post_meta( get_queried_object_id(), '_roundup_ids', true ); // Array of post IDs, in order.
if ( is_singular( 'post' ) && is_array( $ids ) && count( $ids ) >= 2 ) {
$items = [];
foreach ( array_values( $ids ) as $i => $id ) {
$items[] = [ '@type' => 'ListItem', 'position' => $i + 1, 'url' => get_permalink( $id ) ];
}
$schemas[] = [ '@context' => 'https://schema.org', '@type' => 'ItemList', 'itemListElement' => $items ];
}
return $schemas;
} );
Generating the list from the same data that renders the page keeps the order and count in step when you add or remove a recipe.
Checking a carousel candidate
Paste the roundup's JSON-LD into the JSON-LD checker to catch syntax errors. Then do the checks Google says you must do yourself: count that itemListElement has at least two entries, confirm they share one type, and run each linked URL through the Rich Results Test to see valid Recipe, Course, Restaurant or Movie results. One broken detail page doesn't invalidate the list, but it can't appear as a card.
Common questions
Can I use ItemList for a list of products?
You can add it as schema.org markup, but Google's host carousel supports only courses, movies, recipes and restaurants.
Does the carousel show on desktop?
Google describes host carousels as a list-like rich result that people swipe through on mobile devices.
Do list items have to be on my own site?
Yes. On summary pages, each URL must be on the same domain as the list, or a subdomain or parent domain of it.
How many items do I need?
At least two, and the markup should include every item shown on the page.