Recipes for Other Gallery Types: Event, Course List, Discussion Forum, Q&A, Dataset, Software App and Image Metadata
A type is chosen by what the page really is, not by an appearance in the gallery: an online-only event isn't eligible for Event, a product page's Q&A for QAPage, or book reviews for DiscussionForumPosting. We give Bookstore-based cards for Event, Discussion forum, Q&A and Image metadata, explain which types don't fit Bookstore and the carousel limit in Türkiye, and test the cards with a rule table in xUnit.
Version note: This post is based on Google Search Central's structured data gallery (last updated 16 October 2026) and its Event, Discussion forum, Q&A, Image metadata, Dataset, Course list and Carousel documentation as of 15 November 2026, and on schema.org 30.1; the code was built and run with .NET 10 (SDK 10.0.404) and xUnit v3 4.0.
Guess First
Bookstore, the online bookstore used as the shared example across this site's series, is holding a reading night for Dune in December: it takes place in a hall and is streamed live at the same time. The team adds Event markup to the event page and writes a VirtualLocation with only the stream link as location. The same week, QAPage markup is added to the "Questions about this book" section under the book page, and DiscussionForumPosting markup to the reviews; the argument is "the gallery has 25 features, the more types the more appearances". Question: which of these three pieces of markup is eligible for its gallery feature?
None of them, and for the same reason: the type was chosen for an appearance in the gallery, not for what the page really is. This post is made of short recipe cards that walk through the rest of the gallery from Bookstore's point of view: when it fits, what is required, the smallest JSON-LD, and the common mistake.
The Gallery Today
Google's "Structured data markup that Google Search supports" page lists 25 features as of its 16 October 2026 update. This series has covered nine of them: Article, Breadcrumb, Organization and Profile page in the third post; Video, Software app and Speakable in the fourth; Product in the sixth; Review snippet in the seventh. This post gives cards for Event, Discussion forum, Q&A and Image metadata, takes a short look at Dataset, Course list and Carousel, and collects in a table why the rest don't fit Bookstore.
What isn't on the list says something too. The Book actions documentation still exists but the feature is not in the gallery; as we saw in the sixth post, it requires a feed and a partnership. The story of FAQ and HowTo is in the fourth post: the gallery is not a fixed list, and for every type the question starts with "is it supported today?"
This section is based on the official documentation.
Card: Event
When it fits: an event at a physical location, on a specific date, that anyone can attend. Google changed the documentation in June 2025: the online event properties were removed, and to be eligible for Google's event experience an event must be bookable by the general public and take place at a physical location. Events that require a membership or an invitation, and virtual experiences with no real-world component, are not eligible. Each event needs its own URL focused on that single event; schedule pages don't count.
Required: name, startDate, location (a Place) and location.address. Recommended: description, endDate, eventStatus, image, location.name, offers, organizer, performer, previousStartDate. The card for Bookstore's reading night:
{
"@context": "https://schema.org",
"@type": "Event",
"name": "Dune Reading Night: Q&A with Ada Kaplan",
"description": "A one-evening reading of the first chapter of Dune, followed by questions from readers.",
"startDate": "2026-12-12T19:00:00+03:00",
"endDate": "2026-12-12T21:00:00+03:00",
"eventStatus": "https://schema.org/EventScheduled",
"location": {
"@type": "Place",
"name": "Sample Culture Center",
"address": {
"@type": "PostalAddress",
"streetAddress": "Sample Street 1",
"addressLocality": "Istanbul",
"addressCountry": "TR"
}
},
"image": ["https://bookstore.example/images/events/dune-reading-night-16x9.jpg"],
"organizer": { "@id": "https://bookstore.example/#organization" },
"performer": { "@type": "Person", "name": "Ada Kaplan" },
"offers": {
"@type": "Offer",
"url": "https://bookstore.example/events/dune-reading-night",
"price": 0,
"priceCurrency": "TRY",
"availability": "https://schema.org/InStock",
"validFrom": "2026-11-20T10:00:00+03:00"
}
}
Common mistake: putting the venue name, a price or a call such as "buy tickets now" into the event's name. Google wants name to be only the name of the event, with the venue in location.name. Watch the dates too: if you write a time, add the UTC offset, or write no time at all; a value like 2026-12-12T00:00:00+00:00 means 03:00 in the morning for an event in Istanbul, not midnight. For a free event price can be zero; the registration page goes into offers.url. The stream link has no place in this markup; a physical event that is also streamed doesn't become an online event, but an event that is only a stream is not eligible.
Card: Discussion Forum
When it fits: forum pages where users share their own experiences. Google's documentation lists valid and invalid uses explicitly: a community forum where users talk about a game is valid; an article or blog post written by the site owner or their agents, even with comments, is invalid; user reviews about a product are invalid too. The second mistake in the opening is here: book reviews are not DiscussionForumPosting, they are the Review from the seventh post.
Required: author and author.name, datePublished, plus one of text, image or video; the same three for comments (Comment). If Bookstore had a reader forum, a thread page would look like this:
{
"@context": "https://schema.org",
"@type": "DiscussionForumPosting",
"headline": "Should I read the Dune appendices first?",
"text": "I am about to start Dune. A friend says reading the appendices first spoils nothing and helps with the names. Did anyone try that?",
"url": "https://bookstore.example/forum/dune/read-appendices-first",
"datePublished": "2026-11-10T21:04:00+03:00",
"author": {
"@type": "Person",
"name": "Alan T.",
"url": "https://bookstore.example/forum/users/alan-t"
},
"commentCount": 1,
"comment": [
{
"@type": "Comment",
"text": "I did. The glossary helped, the history section was better after the book.",
"datePublished": "2026-11-10T22:30:00+03:00",
"author": {
"@type": "Person",
"name": "Grace H.",
"url": "https://bookstore.example/forum/users/grace-h"
}
}
]
}
For this type Google, against its general preference, recommends Microdata or RDFa so the long text isn't duplicated in the markup; JSON-LD is still fully supported. In March 2026 new properties were added to clarify the structure of comment threads; if content was generated by an AI model, digitalSourceType can say so. If the forum is mainly questions and answers, Google recommends Q&A markup instead.
Card: Q&A
When it fits: a page with a single question and the answers users wrote to it. Google's valid examples include a product support page where users can submit answers to a single question; its invalid examples include an FAQ page written by the site itself, a product page where users can ask and answer several questions on one page, and a blog post or guide that answers a question. That is why the "Questions about this book" section in the opening is not eligible: one page holds several questions.
Required: a single Question under mainEntity, its name and answerCount, at least one of acceptedAnswer or suggestedAnswer, and the text of every Answer. The card, for a structure that gives each question its own page:
{
"@context": "https://schema.org",
"@type": "QAPage",
"mainEntity": {
"@type": "Question",
"name": "Does the Dune paperback include the maps?",
"text": "I want the edition with the map of Arrakis. Does the paperback (ISBN 9780000000019) have it?",
"answerCount": 2,
"datePublished": "2026-11-05T10:12:00+03:00",
"author": { "@type": "Person", "name": "Linus T." },
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, the map is on the two pages before chapter one, in black and white.",
"datePublished": "2026-11-05T11:40:00+03:00",
"upvoteCount": 4,
"url": "https://bookstore.example/questions/dune-paperback-maps#answer-2",
"author": { "@type": "Person", "name": "Ada L." }
},
"suggestedAnswer": [
{
"@type": "Answer",
"text": "Mine has it too, but the hardcover prints it larger.",
"datePublished": "2026-11-06T09:02:00+03:00",
"upvoteCount": 1,
"url": "https://bookstore.example/questions/dune-paperback-maps#answer-3",
"author": { "@type": "Person", "name": "Barbara L." }
}
]
}
}
acceptedAnswer must be an answer accepted on the site in some way: by the asker, a moderator or a voting system. Google asks that other orderings such as "most recent" not be marked as the accepted answer. Comments on answers are Comment, not Answer. Education Q&A, a separate feature in Google's gallery, is for student homework questions and allows a single expert answer; it isn't Bookstore's subject.
Card: Image Metadata
When it fits: when you want Google Images to show an image's creator, copyright information and how it can be licensed. What makes sense for Bookstore is its own product photos. The book's cover design doesn't belong to Bookstore; Bookstore can only state the license of its own photo.
Required: contentUrl and at least one of creator, creditText, copyrightNotice or license. Google writes that once you add one of them, the other three show up as recommended in the Rich Results Test. For the image to appear with the "Licensable" badge, license is required; acquireLicensePage is recommended too:
{
"@context": "https://schema.org",
"@type": "ImageObject",
"contentUrl": "https://bookstore.example/images/books/dune-paperback-1x1.jpg",
"creator": { "@type": "Organization", "name": "Bookstore" },
"creditText": "Photo: Bookstore",
"copyrightNotice": "© 2026 Bookstore",
"license": "https://bookstore.example/image-license",
"acquireLicensePage": "https://bookstore.example/image-license#request"
}
Common mistake: putting the markup on only one page. Google asks for structured data for every instance where the image is used, even if it's the same image. The alternative is to embed the information in the image itself as IPTC fields; if the two conflict, Google uses the structured data.
What Doesn't Fit Bookstore
For the rest of the gallery, knowing why a type doesn't fit is as useful as knowing why it does. Google's general guidelines require structured data to be a true representation of the page content, and as an example of irrelevant markup they give a woodworking site labeling instructions as recipes.
| Feature | Why doesn't it fit Bookstore? |
|---|---|
| Course list | Only in English results; needs at least three courses, carousel markup and a curriculum with instructors and students. Bookstore doesn't sell courses. |
| Dataset | Bookstore doesn't publish a dataset; even if it did, the appearance is limited to Dataset Search. |
| Local business | For physical stores. Bookstore is online only; the right type is the OnlineStore from the eighth post. |
| Recipe, Movie | For recipes and lists of movies; not a bookstore's pages. |
| Job posting, Employer aggregate rating | For job postings and employer ratings; Bookstore's example app has no careers page. |
| Vacation rental | Requires Hotel Center access and a technical partnership with Google. |
| Math solver, Education Q&A | For math problems and student homework questions. |
| Subscription and paywalled content | For paywalled content; Bookstore's pages are open. |
Two types deserve a separate mention. The card for Software app is in the fourth post: the required fields are name, offers.price and a rating or review; without a rating it isn't eligible. Dataset's required fields are name and a description between 50 and 5,000 characters.
Carousel is not a separate feature but a combination. The classic host carousel only works together with Course list, Movie, Recipe and Restaurant. The beta "structured data carousels" combine ItemList with LocalBusiness, Product or Event, but only in the European Economic Area, Türkiye and South Africa, and in Türkiye only for hotel, vacation rental and local business queries. That is why Bookstore's category page is not eligible for a product carousel in Türkiye.
This section is based on the official documentation.
Testing the Cards
The cards are not examples to copy from the documentation and forget; they live as JSON files in Bookstore's test project and go through a small rule table on every build. The rules are a part of the fields Google requires for each feature:
// A few of Google's required properties per feature, read from the Search Central docs on 2026-11-15
public static class GalleryRules
{
public static IReadOnlyList<string> Check(JsonElement node) => node.GetProperty("@type").GetString() switch
{
"Event" => Event(node),
"DiscussionForumPosting" => Forum(node),
"QAPage" => QaPage(node),
"ImageObject" => Image(node),
var other => [$"no rule for {other}"],
};
private static List<string> Event(JsonElement e)
{
List<string> problems = Missing(e, "name", "startDate");
// events must take place at a physical location: a Place with an address
if (!e.TryGetProperty("location", out JsonElement place) || Type(place) != "Place")
{
problems.Add("location must be a Place (virtual-only events are not supported)");
}
else
{
problems.AddRange(Missing(place, "address").Select(p => $"location.{p}"));
}
return problems;
}
private static List<string> Forum(JsonElement post)
{
List<string> problems = Missing(post, "datePublished");
if (!post.TryGetProperty("author", out JsonElement author) || !author.TryGetProperty("name", out _))
{
problems.Add("author.name is missing");
}
if (!HasAny(post, "text", "image", "video"))
{
problems.Add("one of text, image or video is required");
}
return problems;
}
private static List<string> QaPage(JsonElement page)
{
// exactly one Question per page, under mainEntity
if (!page.TryGetProperty("mainEntity", out JsonElement q) || q.ValueKind != JsonValueKind.Object
|| Type(q) != "Question")
{
return ["mainEntity must be a single Question"];
}
List<string> problems = Missing(q, "name", "answerCount");
if (!HasAny(q, "acceptedAnswer", "suggestedAnswer"))
{
problems.Add("a Question needs acceptedAnswer or suggestedAnswer");
}
foreach (JsonElement answer in Answers(q))
{
problems.AddRange(Missing(answer, "text").Select(p => $"Answer.{p}"));
}
return problems;
}
The tests go both ways: good cards must produce no problems, and cards with known mistakes must produce the expected problem.
using System.Text.Json;
namespace Bookstore.Web.Tests;
public sealed class GalleryCardTests
{
private static JsonElement Load(string file) =>
JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "GalleryCards", file))).RootElement;
[Theory]
[InlineData("event.json")]
[InlineData("discussion-forum.json")]
[InlineData("qa-page.json")]
[InlineData("image-metadata.json")]
public void Card_meets_the_required_properties(string file)
{
JsonElement card = Load(file);
Assert.Equal("https://schema.org", card.GetProperty("@context").GetString());
Assert.Empty(GalleryRules.Check(card));
}
[Theory]
[InlineData("event-online-only.json", "location must be a Place (virtual-only events are not supported)")]
[InlineData("event-online-only.json", "name is missing")]
[InlineData("qa-product-page.json", "mainEntity must be a single Question")]
public void Rule_catches_a_known_mistake(string file, string expected)
{
Assert.Contains(expected, GalleryRules.Check(Load(file)));
}
}
Putting the opening's event card, written with only the stream link, among the good cards gives:
failed Bookstore.Web.Tests.GalleryCardTests.Card_meets_the_required_properties(file: "event-online-only.json") (0ms)
Assert.Empty() Failure: Collection was not empty
Collection: ["name is missing", "location must be a Place (virtual-only events are "···]
$ dotnet test --project Bookstore.Web.Tests
Test run summary: Passed!
total: 35
failed: 0
succeeded: 31
skipped: 4
These rules don't replace Google's decision; as we said in the fifth post, only Google's own tools tell you about eligibility. Their purpose is to catch, at build time, a requirement the documentation states plainly but someone forgot.
Production Notes
Follow the gallery regularly. Google's changelog (developers.google.com/search/updates) also offers an RSS feed; every date in this series comes from it. The changes in 2025 and 2026 showed that support for a type can change within a few months: Event's online properties were removed, store-level shipping policies were added, and a new guideline entered the review snippet documentation.
When you move a card to a page, tie it to the visible content. The JSON files here are test data; when a real event or forum page is written, the markup must be produced from the page's own data, like the price in the sixth post, and a test must show that it matches the page.
Update the rule table together with the documentation. A rule that demands something Google no longer requires gives false confidence; the dated comment line is there as a reminder.
Common Pitfalls
Adding a type unrelated to the content "to get a rich result". Google's general guidelines may treat irrelevant markup as spam. A structured data manual action doesn't remove the page from Search, but the structured data on the page is ignored.
Marking an online-only event as Event. Not eligible since June 2025.
Turning the Q&A section of a product page into a QAPage. One page holds several questions; Google gives this explicitly as an invalid example.
Marking book reviews as DiscussionForumPosting. User reviews of a product are not a forum; they are a Review.
Writing a license on the cover image. The rights to the cover design belong to the publisher and its designer; Bookstore can only license its own photo.
A Small Experiment
★☆☆ Postpone the event by a week by adding a previousStartDate and eventStatus: EventRescheduled to event.json. Google asks that startDate not be removed even when the status changes; why?
★★☆ Add a rule for Dataset to GalleryRules: name and description are required, and description must be between 50 and 5,000 characters. Test the rule with one good and one bad card.
★★★ Move the reading night into a real Razor page in Bookstore (/events/dune-reading-night); produce the markup from the page's own data and test with JsonLdPage that name, startDate and the venue name match the text visible on the page.
The series ends here. We started from what structured data is and how to generate JSON-LD safely; we built an entity graph for a tech blog, saw what Google supports and what it has retired, and tied it all to tests. In the last four posts we moved to Bookstore's store side: the book page's price and stock, reader reviews and the rating, the store's return and shipping policy, and the rest of the gallery. All the posts share one lesson: markup must be produced from the page's own data, and a test must show that it matches the page. The natural next step is the content you mark up; the Content SEO series being written in this round covers it.