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.

json
{
  "@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

PropertyStatusWhat it is
itemListElementRequiredTwo or more ListItem entries, all describing the same type. Don't mix recipes and restaurants in one list.
ListItem.positionRequiredThe item's place in the list, starting at 1. Google shows items in this order.
ListItem.urlRequiredSummary 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.itemRequiredAll-in-one pages only: the full object, with name, url and the properties its type requires.
item.urlRecommendedOn an all-in-one page, an anchor on the same page, such as #lemon-bars.
item.nameRecommendedThe 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.

LayoutWhat the page containsWhat each ListItem holds
Summary pageA short blurb per item, each linking to a separate detail pageOnly @type, position and url
All-in-one pageThe full content of every item on one pageposition 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:

php
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.

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.