Schema.org JSON-LD
Maps ActivityPub actors, objects, and activities to Schema.org JSON-LD.
{
"@context": "https://schema.org",
"@type": "Person",
"@id": "https://social.example/users/ada",
"name": "Ada Okoye",
"alternateName": "@ada@social.example",
"identifier": "acct:ada@social.example",
"description": "Front-end engineer. Building small tools for large groups of people.",
"url": "https://social.example/users/ada",
"image": {
"@type": "ImageObject",
"contentUrl": "https://api.dicebear.com/9.x/thumbs/svg?seed=ada",
"url": "https://api.dicebear.com/9.x/thumbs/svg?seed=ada",
"caption": "Ada Okoye avatar",
"description": "Ada Okoye avatar",
"encodingFormat": "image/svg+xml"
},
"sameAs": [
"https://ada.example"
],
"interactionStatistic": [
{
"@type": "InteractionCounter",
"interactionType": {
"@type": "FollowAction"
},
"userInteractionCount": 3187
},
{
"@type": "InteractionCounter",
"interactionType": {
"@type": "SubscribeAction"
},
"userInteractionCount": 291
},
{
"@type": "InteractionCounter",
"interactionType": {
"@type": "WriteAction"
},
"userInteractionCount": 412
}
],
"additionalProperty": [
{
"@type": "PropertyValue",
"name": "Website",
"value": "https://ada.example"
},
{
"@type": "PropertyValue",
"name": "Location",
"value": "Lagos, Nigeria"
},
{
"@type": "PropertyValue",
"name": "Pronouns",
"value": "she/her"
}
]
}{
"@context": "https://schema.org",
"@type": "SocialMediaPosting",
"@id": "https://fediverse.example/users/mira/posts/coastline-drift",
"url": "https://fediverse.example/users/mira/posts/coastline-drift",
"articleBody": "Spent the week redrawing a 1:25000 coastline by hand. The shoreline has moved 40 metres inland since the 1984 survey.\n\nEvery map is a snapshot of a place that has already changed. #cartography #openstreetmap",
"text": "Spent the week redrawing a 1:25000 coastline by hand. The shoreline has moved 40 metres inland since the 1984 survey.\n\nEvery map is a snapshot of a place that has already changed. #cartography #openstreetmap",
"datePublished": "2025-06-12T14:26:00.000Z",
"inLanguage": "en",
"author": {
"@type": "Person",
"@id": "https://fediverse.example/users/mira",
"name": "Mira Halvorsen",
"alternateName": "@mira@fediverse.example",
"identifier": "acct:mira@fediverse.example",
"description": "Cartographer. I make maps of things that are hard to see.",
"url": "https://fediverse.example/users/mira",
"image": {
"@type": "ImageObject",
"contentUrl": "https://api.dicebear.com/9.x/thumbs/svg?seed=mira",
"url": "https://api.dicebear.com/9.x/thumbs/svg?seed=mira",
"caption": "Mira Halvorsen avatar",
"description": "Mira Halvorsen avatar",
"encodingFormat": "image/svg+xml"
},
"sameAs": [
"https://maps.example/mira"
],
"additionalProperty": [
{
"@type": "PropertyValue",
"name": "Atlas",
"value": "https://maps.example/mira"
},
{
"@type": "PropertyValue",
"name": "Location",
"value": "Bergen, Norway"
}
]
},
"image": [
{
"@type": "ImageObject",
"contentUrl": "https://api.dicebear.com/9.x/shapes/svg?seed=coastline",
"url": "https://api.dicebear.com/9.x/shapes/svg?seed=coastline",
"caption": "Hand-drawn coastline contour map with annotated survey markers",
"description": "Hand-drawn coastline contour map with annotated survey markers",
"width": 1200,
"height": 800
},
{
"@type": "ImageObject",
"contentUrl": "https://api.dicebear.com/9.x/shapes/svg?seed=survey",
"url": "https://api.dicebear.com/9.x/shapes/svg?seed=survey",
"caption": "Comparison overlay of the 1984 and current shoreline",
"description": "Comparison overlay of the 1984 and current shoreline",
"width": 1200,
"height": 800
}
],
"keywords": [
"cartography",
"openstreetmap"
],
"contentLocation": {
"@type": "Place",
"name": "Bergen, Norway",
"geo": {
"@type": "GeoCoordinates",
"latitude": 60.39,
"longitude": 5.32
}
},
"commentCount": 2,
"interactionStatistic": [
{
"@type": "InteractionCounter",
"interactionType": {
"@type": "LikeAction"
},
"userInteractionCount": 214
},
{
"@type": "InteractionCounter",
"interactionType": {
"@type": "ShareAction"
},
"userInteractionCount": 38
},
{
"@type": "InteractionCounter",
"interactionType": {
"@type": "CommentAction"
},
"userInteractionCount": 2
}
]
}Installation
npx shadcn@latest add https://ui.uptoolkit.com/r/schema-org.jsonUsage
ActivityPub gets your content to other servers; Schema.org gets it into search results and AI answers. Both are JSON-LD, so the same source object projects into either shape without a second source of truth.
Every function is pure, takes an ActivityPub document, and returns a plain JSON-LD node. undefined,
null, and empty arrays are dropped, so output stays small enough to inline.
| ActivityPub | Schema.org |
|---|---|
Person | Person |
Group, Organization | Organization |
Note | SocialMediaPosting |
Article | BlogPosting |
reply Note | Comment with parentItem |
| actor profile | ProfilePage with mainEntity |
OrderedCollection | CollectionPage wrapping an ItemList |
Like, Announce, Follow | LikeAction, ShareAction, FollowAction |
import { JsonLd } from "@/components/json-ld";
import { toProfilePageJsonLd, toSocialMediaPostingJsonLd } from "@/lib/schema-org";
<JsonLd data={toSocialMediaPostingJsonLd(note, { includeComments: true })} />
<JsonLd data={toProfilePageJsonLd(actor, { counts: { followers: 3187 }, posts })} />Counts become interactionStatistic entries with the right InteractionCounter action type, which
is what Google reads for social engagement signals:
toInteractionStatisticJsonLd({ likes: 214, shares: 38, replies: 12, followers: 3187 });Set withContext: false on any call to omit @context when nesting a node inside a larger graph.
toPersonJsonLd emits the WebFinger acct: URI as identifier, so a Schema.org consumer can join
the node back to the fediverse identity.