toddlevy/tl-agent-skills

tl-schema-org

The full Schema.org vocabulary -- all 800+ types, 1500+ properties -- with production patterns for JSON-LD rendering, database modeling, API interoperability, extension governance, and rich results.

Zobacz źródło
Oryginalny dokument Skill

Treść z repozytorium z zachowaniem nagłówków, przykładów, kodu, tabel, linków i obrazów.

<!-- Copyright (c) 2026 Todd Levy. Licensed under MIT. SPDX-License-Identifier: MIT -->

Schema.org

Work fluently with the entire Schema.org vocabulary -- types, properties, enumerations, and their relationships -- across every surface where structured data matters: web pages, databases, APIs, and data interchange.

When to Use

  • "Add structured data to a page"
  • "Map Schema.org types to a database"
  • "Design an API using Schema.org vocabulary"
  • "Extend Schema.org with custom properties"
  • "Which Schema.org type should I use for X?"
  • "Validate structured data markup"
  • Working with JSON-LD, RDFa, or any semantic/linked-data integration
  • Building data models grounded in a shared vocabulary

Outcomes

  • Artifact: JSON-LD markup, database schemas, API type definitions, or extension specifications aligned to Schema.org
  • Decision: Type selection, extension strategy, rendering approach, or validation plan

1. Schema.org Fundamentals

Schema.org is a collaborative vocabulary of 800+ types and 1500+ properties maintained by Google, Microsoft, Yahoo, and Yandex. It is not a rigid ontology -- it follows Postel's Law: be liberal in what you accept, conservative in what you produce.

Data Model

  • Types form a hierarchy rooted at Thing. A type can have multiple parent types (multiple inheritance).
  • Properties have one or more domain types (where they can appear) and one or more range types (what values they accept).
  • Enumerations are types whose instances are a fixed set of members (e.g., ItemAvailability has InStock, OutOfStock, etc.).
  • Conformance is pragmatic: search engines accept text strings where a type is expected, and properties can appear on types outside their declared domain.

Hierarchy at a Glance

Everything descends from Thing. The major branches:

BranchKey TypesTypical Use
ActionAchieveAction, TradeAction, SearchActionUser interactions, deep linking
CreativeWorkArticle, Book, MusicComposition, SoftwareApplicationContent, media, publications
EventMusicEvent, SportsEvent, FestivalHappenings with dates and locations
IntangibleOffer, Order, Rating, StructuredValueCommerce, measurements, abstract concepts
MedicalEntityMedicalCondition, Drug, MedicalProcedureHealth and medical content
OrganizationCorporation, LocalBusiness, SportsTeamEntities with structure and identity
Person--People with roles and relationships
PlaceMusicVenue, Restaurant, City, CountryPhysical and administrative locations
ProductProductModel, ProductGroup, VehicleTangible goods and variants
BioChemEntityGene, Protein, MolecularEntityLife sciences

For the complete hierarchy, see assets/tree.jsonld. For type/property lookup, query assets/schemaorg-current-https-types.csv and assets/schemaorg-current-https-properties.csv.

See: references/taxonomy-guide.md

Domain Clusters

Common verticals and their Schema.org type constellations:

VerticalPrimary TypesSupporting Types
E-commerceProduct, Offer, AggregateOfferBrand, Organization, QuantitativeValue, SizeSpecification
EventsEvent, MusicEvent, FestivalPlace, PostalAddress, GeoCoordinates, Offer, Person
News/BlogArticle, BlogPosting, NewsArticlePerson, Organization, ImageObject, WebPage
Books & PublishingBook, BookSeriesPerson, Organization, Offer, ImageObject, PublicationVolume, PublicationIssue
JobsJobPostingOrganization, Place, MonetaryAmount
Local BusinessLocalBusiness, RestaurantPostalAddress, GeoCoordinates, OpeningHoursSpecification
EducationCourse, LearningResourceOrganization, Person, Offer
RecipesRecipeNutritionInformation, HowToStep, ImageObject
CollectiblesProduct, ProductModel, ProductGroupOffer, Brand, QuantitativeValue, PropertyValue
MusicMusicGroup, MusicEvent, MusicCompositionPerson, Place, Offer, MusicAlbum

2. JSON-LD Rendering

JSON-LD is the recommended format for structured data on web pages. It separates structured data from HTML, making it easier to maintain and less coupled to markup changes.

Core Patterns

Single entity:

json
{
  "@context": "https://schema.org",
  "@type": "Event",
  "name": "Summer Jazz Festival",
  "startDate": "2026-07-15T19:00:00-05:00",
  "location": {
    "@type": "MusicVenue",
    "name": "Riverside Amphitheater",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "100 River Road",
      "addressLocality": "Austin",
      "addressRegion": "TX",
      "postalCode": "78701",
      "addressCountry": "US"
    }
  }
}

Multi-entity with `@graph` and `@id` cross-references:

json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#org",
      "name": "Riverside Concerts",
      "url": "https://example.com"
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#site",
      "name": "Riverside Concerts",
      "url": "https://example.com",
      "publisher": { "@id": "https://example.com/#org" }
    },
    {
      "@type": "Event",
      "name": "Summer Jazz Festival",
      "organizer": { "@id": "https://example.com/#org" }
    }
  ]
}

Multi-type entities (an item that is simultaneously two types):

json
{
  "@context": "https://schema.org",
  "@type": ["Book", "Product"],
  "name": "The Complete Jazz Standards",
  "isbn": "978-0-123456-78-9",
  "offers": { "@type": "Offer", "price": "29.95", "priceCurrency": "USD" }
}

Enumeration Values

Always use full Schema.org URIs for enumeration values:

json
"availability": "https://schema.org/InStock",
"eventStatus": "https://schema.org/EventScheduled",
"itemCondition": "https://schema.org/NewCondition",
"eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode"

Placement and Rendering

  • Place JSON-LD in <head> or before </body> inside <script type="application/ld+json">
  • For SSR: build the structured data object in your route handler or getMetaTags-style function, then inject it into the HTML template as a single script tag
  • For SPAs: inject dynamically via useEffect or equivalent lifecycle hook
  • One <script type="application/ld+json"> tag per page is cleanest; use @graph to combine multiple entities

See: references/json-ld-patterns.md


3. Extension Patterns

Schema.org is intentionally incomplete. Real-world domains always have properties that the vocabulary doesn't cover. The question is how to handle them.

Decision Framework

Before extending, work through this hierarchy:

  1. Use an existing property. Check the CSV data files -- Schema.org has 1500+ properties. The one you need may already exist under a different name.
  2. Use `additionalProperty` with `PropertyValue`. For domain-specific attributes that don't justify a custom field (purity, mintage, face value, finish):
json
   "additionalProperty": [
     { "@type": "PropertyValue", "name": "Purity", "value": ".9999" },
     { "@type": "PropertyValue", "name": "Mintage", "value": 50000 }
   ]
  1. Use `x-` prefixed extensions for properties that consumers of your data need but Schema.org doesn't define. These are public, part of your API contract.
  2. Use `_x-` prefixed extensions for internal/admin-only fields that should never reach external consumers.

Two-Tier Extension Convention

PrefixVisibilityPurposeExample
x-PublicConsumer-facing domain extensionsx-abstract, x-slug, x-headliner
_x-InternalAdmin, debug, pipeline metadata_x-displayOrder, _x-lastChecked, _x-sellerId

Field Ordering

Schema.org-grounded JSON responses should order fields predictably:

PriorityCategoryExample
0@context"https://schema.org"
1@type"Product"
2Standard Schema.org propertiesname, description, offers
3x- public extensionsx-slug, x-abstract
4_x- internal extensions_x-displayOrder
5Other hidden fields_internal, _hidden

Stripping Internal Fields

For public API responses, recursively remove _x- prefixed fields. This keeps your internal metadata (display order, pipeline timestamps, seller IDs) out of consumer-facing payloads while preserving them for admin/debug endpoints.

External Vocabularies

When Schema.org genuinely lacks coverage for your domain, consider established external vocabularies (e.g., gs1.org/voc for supply chain, musicontology.com for music) before inventing your own. These can coexist with Schema.org in a JSON-LD context.

Pending Terms

Schema.org uses a "pending" label for experimental terms. These are safe to use but may be renamed, restructured, or dropped. Pin to a specific Schema.org version if stability matters.

See: references/extension-patterns.md


4. Database Modeling

Schema.org is a vocabulary, not a database schema. Use it as design inspiration and naming convention. Map Schema.org types to tables, optimize for your query patterns, and store domain-specific enums in tables that get translated to Schema.org URIs at serialization time.

See Database Modeling for the full type-to-table strategies, product variant architecture, enum mapping tables (availability, condition, event status), DB-driven property routing for measurements, UN/CEFACT unit-code mapping, polymorphic relationship patterns, identifier strategies, and column naming conventions.

Books, Editions & Series

Books are the canonical case where Schema.org splits one real-world "book" into two distinct entities, and getting this wrong is the most common book-modeling mistake:

  • The work (Book as abstract creation): the title, author, subject -- the thing that persists across every printing.
  • The edition (Book linked by bookEdition): a specific ISBN, format, publisher, page count, publication year. One work has many editions.
  • Work <-> edition link: workExample (work -> its editions) and its inverse exampleOfWork (edition -> its work). This is the Book-domain analog of ProductModel/isVariantOf.
  • The series (BookSeries): groups volumes via hasPart/isPartOf + position (or volumeNumber). A multi-volume set (Vol. I, Vol. II) is one BookSeries with N member Book works -- never a single conflated entity.
  • Format is an enumeration (bookFormat -> BookFormatType: Hardcover, Paperback, EBook, AudiobookFormat, GraphicNovel), belonging on the edition, not the work.
See Book Modeling for the full work/edition/series treatment: the two-level identity model, the DB table layout (books / editions / series / contributors / offers), BookFormatType mapping, typed contributor roles (author/editor/illustrator/foreword vs. subject), library/CDL borrow-and-read Offer modeling, JSON-LD output patterns, and the anti-patterns (grouped multi-title entries, format-on-the-work, volumes-as-one-row) that silently drop or merge books.

Series & Family Grouping (domain-general)

BookSeries is one instance of a cross-domain pattern: a named, numbered run of otherwise-independent works. The same shape recurs for music (a live-release run like Dick's Picks Vol. 1..36 or Dave's Picks Vol. 1..58), TV (CreativeWorkSeries with TVSeason/TVEpisode), periodicals (PublicationVolume/PublicationIssue), and podcasts (PodcastSeries). Model it the same way every time:

  • The run is its own entity, never a conflated row. Each member is a first-class work with its own identity, cover, date, and links; the series is a separate record that groups them. Collapsing "Dick's Picks" into one release row (or one book row) is the volumes-as-one-row anti-pattern -- it silently drops every individual volume's data.
  • Schema.org type: CreativeWorkSeries is the general super-type (BookSeries, Periodical, PodcastSeries, RadioSeries, TVSeries are its specializations). A live-album run has no dedicated subtype -- use CreativeWorkSeries and let each member be a MusicAlbum/LiveAlbum.
  • Membership + order: link with hasPart (series -> members) / isPartOf (member -> series), and carry the member's rank in position (or the domain-specific volumeNumber). A member with an unknown rank keeps a null position -- never a fabricated one.
  • DB shape (the recurring three-part layout): a <domain>_series table (id, slug natural key, name, description), plus a nullable series_id FK + nullable volume_position on the member table. slug is the seed-join natural key so the series row always precedes the members that reference it; ON DELETE SET NULL keeps a series merge from orphaning members. This mirrors the books layout (*_series + series_id/position on the work) and generalizes it to any run.
  • Derive membership, don't hand-curate it, when the run is already encoded in identity. If members carry a <series>-volume-<n> slug (or a <name> Volume <n> title), the series and each member's position are derivable from that convention -- emit them from a generator over the committed member seed rather than maintaining a parallel hand-list that drifts. Hand-curate only when the grouping is not mechanically recoverable.
  • Substrate note (VIP): in the @vip-suite/* stack the durable cross-domain home for this is the product-family layer (ProductGroup/ProductModel + isVariantOf); until a vertical's series needs promotion, the per-consumer <domain>_series + series_id/position shape above is the correct spoke-local implementation, and the substrate absorbs it when a second vertical wants the same grouping.

5. API Interoperability

Schema.org provides a shared vocabulary that makes APIs interoperable without requiring full JSON-LD compliance. The spectrum runs from "Schema.org-inspired property names" to "full JSON-LD responses."

OpenAPI Type Hierarchy

Mirror Schema.org's inheritance in OpenAPI using allOf:

yaml
Thing:
  type: object
  properties:
    name: { type: string }
    identifier: { type: string }
    url: { type: string }
    image: { type: string }
    sameAs: { type: array, items: { type: string } }
    datePublished: { type: string, format: date-time }
    dateModified: { type: string, format: date-time }

Event:
  allOf:
    - $ref: '#/components/schemas/Thing'
    - type: object
      properties:
        startDate: { type: string, format: date-time }
        endDate: { type: string, format: date-time }
        eventStatus: { type: string }
        location: { $ref: '#/components/schemas/Place' }
        offers: { type: array, items: { $ref: '#/components/schemas/Offer' } }
        performer: { type: array, items: { $ref: '#/components/schemas/Person' } }

Response Format Spectrum

Approach@context@typeWhen to Use
Full JSON-LDYesYesPublic APIs consumed by search engines, AI systems, or linked-data clients
Schema.org-inspiredNoYesInternal/partner APIs that benefit from shared vocabulary without RDF overhead
Property names onlyNoNoAPIs that use Schema.org naming conventions for interoperability

Cross-System Identifier Mapping

When your entities exist in multiple systems, use structured external identifiers:

json
"x-externalIdentifiers": [
  { "source": "ticketmaster", "identifier": "K8vZ9175st0" },
  { "source": "musicbrainz", "identifier": "f59c5520-5f46-4d2c-b2c4-822eabf53419" }
]

This enables cross-service resolution without coupling to any single provider's ID scheme. Use sameAs for canonical web URLs of the same entity on other platforms.

See: references/api-interoperability.md


6. Rich Results and SEO

Structured data enables rich results in search engines -- FAQ dropdowns, star ratings, product cards, event listings. This is the most visible consumer of Schema.org markup.

Required vs Recommended

Each rich result type has required and recommended properties. Missing a required property suppresses the entire rich result. The more recommended properties you include, the higher quality the result.

TypeRequiredRecommended
Productname, image, offers (with price + availability)brand, aggregateRating, review, sku
Articleheadline, image, datePublished, authordateModified, publisher, description
Eventname, startDate, locationendDate, offers, performer, image, eventStatus
FAQPagemainEntity (Question/Answer array)--
LocalBusinessname, addressgeo, openingHours, telephone, aggregateRating
Recipename, imagecookTime, nutrition, recipeIngredient, recipeInstructions

Validation, Common Errors, Quality Rules

See Rich Results for the full 5-step validation workflow (validator.schema.org, Google Rich Results Test, Search Console), the common-errors fix table, and the quality rules that govern when structured data is allowed.

7. Version Tracking and Governance

Schema.org publishes numbered releases every few weeks. The vocabulary grows but rarely removes terms — deprecated types move to an "attic" rather than being deleted.

See Version Tracking for the release-tracking script (scripts/update-schema-data.sh writes assets/VERSION), CSV diff strategies, the impact-assessment matrix by change type, and the migration workflow when your x- extension becomes an official Schema.org property.

Verification

Structured Data Implementation

  • [ ] JSON-LD is valid JSON (syntax check)
  • [ ] @context is "https://schema.org"
  • [ ] @type uses correct Schema.org type name
  • [ ] Enumeration values use full URIs
  • [ ] All required properties for target rich results are present
  • [ ] Dates use ISO 8601 format
  • [ ] URLs are fully qualified
  • [ ] Content matches visible page content
  • [ ] Validates in Schema.org Validator without errors

Database Model Alignment

  • [ ] Table names correspond to Schema.org types
  • [ ] Column names use Schema.org property names where applicable
  • [ ] Enum mapping tables cover all domain values
  • [ ] Polymorphic relationships handled (e.g., performer: Person | Organization)
  • [ ] Identifier strategy supports internal IDs, slugs, and external system IDs

API Interoperability

  • [ ] Response types mirror Schema.org type hierarchy
  • [ ] Property names match Schema.org vocabulary
  • [ ] Extension fields use x- prefix convention
  • [ ] Internal fields use _x- prefix and are stripped from public responses
  • [ ] Field ordering follows convention (@context > @type > standard > x- > _x-)

Extension Governance

  • [ ] Existing Schema.org properties checked before creating extensions
  • [ ] additionalProperty used for one-off domain attributes
  • [ ] x- prefix used for recurring public extensions
  • [ ] _x- prefix used for internal-only fields
  • [ ] External vocabularies considered for domain-specific gaps
z tego samego repozytorium

Więcej Skills

Wszystkie Skills