Skip to content

Mental model

unschema-graph has four concepts: a builder validates input, an entity describes one thing, an identity lets other entities point to that thing, and a graph collects the connected entities in one JSON-LD document.

page or CMS data
↓
Article({ ... }) builder validates the input
↓
{ "@type": "Article" } entity with a Schema.org type
↓
buildJsonLdGraph(items) identities are resolved and duplicate nodes merge
↓
serializeJsonLd(payload) HTML-sensitive characters are escaped
↓
<script type="application/ld+json">

Astro and Svelte run the final two steps inside <Schema />. Core exposes the same steps directly.

A builder is a callable, typed validator such as Article, Organization, or Product. It owns the final @type; you provide the properties for that type.

import { Article } from '@unschema-graph/core';
const article = Article({
headline: 'Connected structured data',
image: 'https://example.com/cover.jpg',
datePublished: '2026-09-29',
author: 'Ada Lovelace',
});

TypeScript checks code you write. Zod validates the data when the builder runs, which also covers values loaded from a CMS or API.

The value returned by a builder is an entity. Add @id when that entity must be referenced from another node or reused across pages.

const organization = Organization({
'@id': '#organization',
name: 'Acme Publishing',
url: 'https://example.com',
});
const article = Article({
headline: 'Connected structured data',
image: 'https://example.com/cover.jpg',
datePublished: '2026-09-29',
author: 'Ada Lovelace',
publisher: '#organization',
});

publisher: '#organization' becomes an @id reference instead of a second embedded copy of the organization.

One page can describe the site, publisher, page, and article together:

import {
Article,
Organization,
WebPage,
WebSite,
buildJsonLdGraph,
} from '@unschema-graph/core';
const organization = Organization({
'@id': '#organization',
name: 'Acme Publishing',
url: 'https://example.com',
});
const website = WebSite({
'@id': '#website',
name: 'Acme Journal',
url: 'https://example.com',
publisher: '#organization',
});
const page = WebPage({
'@id': '/articles/graph#webpage',
name: 'Connected structured data',
url: '/articles/graph',
isPartOf: '#website',
});
const article = Article({
'@id': '/articles/graph#article',
headline: 'Connected structured data',
image: 'https://example.com/cover.jpg',
datePublished: '2026-09-29',
author: 'Ada Lovelace',
publisher: '#organization',
mainEntityOfPage: '/articles/graph#webpage',
});
const graph = buildJsonLdGraph([organization, website, page, article], {
baseUrl: 'https://example.com',
});

The graph contains four nodes. Their relative identities become absolute, and references point to the corresponding nodes without duplicating their properties.

unschema-graph validates the properties modeled by each builder, composes the graph, and safely serializes JSON-LD for HTML. It does not guarantee that a search engine will display a rich result or that a platform will consume a particular property.

Next: learn how types and properties are validated.