Custom JSON-LD¶
There are two ways to control structured data with easeo: per-page overrides and registered generators. This guide shows when to use each.
Per-page override¶
Use SEOOverrides when a single page needs a different schema:
Overrides win over every other source and can be a single object or a list of objects.
Registered generator¶
When every page of a given schema type should use the same shape, register a generator once on the config:
from easeo import SEOConfig, SchemaRegistry
registry = SchemaRegistry()
@registry.register("Article")
def podcast_episode(entity, config, canonical, title, description, og_image):
return {
"@context": "https://schema.org",
"@type": "PodcastEpisode",
"name": title,
"url": canonical,
"description": description,
}
config = SEOConfig(
canonical_host="example.com",
public_base_url="https://example.com",
schema_registry=registry,
)
const { SchemaRegistry } = require("@easeo/core");
const registry = new SchemaRegistry();
registry.register("Article", (entity, config, canonical, title) => ({
"@context": "https://schema.org",
"@type": "PodcastEpisode",
name: title,
url: canonical,
}));
const config = {
canonicalHost: "example.com",
publicBaseUrl: "https://example.com",
schemaRegistry: registry,
};
The generator receives (entity, config, canonical, title, description,
og_image) and returns a dict. Return None to fall back to the built-in
schema.
Managing generators¶
| Python | JavaScript | Purpose |
|---|---|---|
register |
register |
Add or replace a generator |
unregister |
unregister |
Remove a generator |
get |
get |
Look up a generator |
has |
has |
Check whether one is registered |
list_types |
listTypes |
List registered type names |
Site-wide injection with a hook¶
To add an Organization schema to every page, use a hook:
from easeo import HookRegistry
hooks = HookRegistry()
@hooks.hook("post_process")
def inject_organization(payload, entity, config):
org = {
"@context": "https://schema.org",
"@type": "Organization",
"name": config.publisher_name or "Example",
"url": config.public_base_url,
}
existing = payload.get("schema_jsonld")
if isinstance(existing, list):
payload["schema_jsonld"] = [org, *existing]
elif existing is not None:
payload["schema_jsonld"] = [org, existing]
else:
payload["schema_jsonld"] = org
return payload
config = SEOConfig(..., hooks=hooks)
Precedence¶
omit_schemaproducesNone.SEOOverrides.schema_jsonld.- A registered generator matching the resolved
@type. - The auto-generated schema.
Breadcrumbs are appended in every case, and hooks run last.
Recap¶
- Use
SEOOverridesfor one page. - Use
SchemaRegistryfor a whole schema type. - Use a hook to inject fields into every payload.