2026-09-02 16:22:52 +01:00
|
|
|
import { SITE_URL, absoluteUrl } from '@/lib/site';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The site's author entity.
|
|
|
|
|
*
|
|
|
|
|
* First name only, by choice — see /about. That makes this a weaker search
|
|
|
|
|
* signal than a fully identified author would be, which is why the About page
|
|
|
|
|
* carries a substantial methodology section: the credibility has to come from
|
|
|
|
|
* stated provenance rather than from a corroborable identity.
|
|
|
|
|
*
|
|
|
|
|
* Everything that needs an author — the About page, every post byline —
|
|
|
|
|
* references this one shape, so search engines resolve them all to one entity.
|
|
|
|
|
*/
|
|
|
|
|
export function personJsonLd() {
|
|
|
|
|
return {
|
|
|
|
|
'@type': 'Person',
|
|
|
|
|
'@id': `${SITE_URL}/about#tudor`,
|
|
|
|
|
name: 'Tudor',
|
|
|
|
|
url: absoluteUrl('/about'),
|
|
|
|
|
image: absoluteUrl('/brand/tudor.jpg'),
|
|
|
|
|
description:
|
|
|
|
|
'Parent in south-west London who built schoolcompare while looking for a primary school.',
|
|
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function organizationJsonLd() {
|
|
|
|
|
return {
|
|
|
|
|
'@type': 'Organization',
|
|
|
|
|
'@id': `${SITE_URL}#organization`,
|
|
|
|
|
name: 'schoolcompare',
|
|
|
|
|
url: SITE_URL,
|
|
|
|
|
logo: absoluteUrl('/icon-512.png'),
|
|
|
|
|
} as const;
|
|
|
|
|
}
|
2026-09-02 16:28:58 +01:00
|
|
|
|
|
|
|
|
interface PostSummary {
|
|
|
|
|
title: string;
|
|
|
|
|
slug: string;
|
|
|
|
|
excerpt: string;
|
|
|
|
|
publishedAt: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* References the Person and Organization by @id rather than repeating them, so
|
|
|
|
|
* search engines resolve every post and the About page to the one author
|
|
|
|
|
* entity. Repeating the shape would declare several people with one name.
|
|
|
|
|
*/
|
|
|
|
|
export function blogPostingJsonLd(post: PostSummary) {
|
|
|
|
|
return {
|
|
|
|
|
'@type': 'BlogPosting',
|
|
|
|
|
headline: post.title,
|
|
|
|
|
description: post.excerpt,
|
|
|
|
|
url: absoluteUrl(`/blog/${post.slug}`),
|
|
|
|
|
datePublished: post.publishedAt,
|
|
|
|
|
author: { '@id': `${SITE_URL}/about#tudor` },
|
|
|
|
|
publisher: { '@id': `${SITE_URL}#organization` },
|
|
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function breadcrumbJsonLd(post: PostSummary) {
|
|
|
|
|
return {
|
|
|
|
|
'@type': 'BreadcrumbList',
|
|
|
|
|
itemListElement: [
|
|
|
|
|
{
|
|
|
|
|
'@type': 'ListItem',
|
|
|
|
|
position: 1,
|
|
|
|
|
name: 'Blog',
|
|
|
|
|
item: absoluteUrl('/blog'),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
'@type': 'ListItem',
|
|
|
|
|
position: 2,
|
|
|
|
|
name: post.title,
|
|
|
|
|
item: absoluteUrl(`/blog/${post.slug}`),
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
} as const;
|
|
|
|
|
}
|