Structured Data Components
Reusable JSON-LD schema builders for rich search results.
Builders & toolkitsLiveReactschema.org
7schema types
What it is
A set of JSX components — Person, Website, Article, Software, Research, Breadcrumb — that each emit a JSON-LD script tag. Pages compose them to earn rich results and breadcrumbs in search.
Take it with you
The real, committed source behind this system — copy it or download the file. Plus a portable spec of everything on this page.
structured-data.jsxjsx305 lines
Reusable JSON-LD components (Person, Website, Article, Software, Breadcrumb …) that each emit a schema.org script tag.
// ---------------------------------------------------------------------------
// lib/structured-data.jsx — JSON-LD schema builders for jakelawrence.xyz
// ---------------------------------------------------------------------------
//
// Usage in any page.jsx:
//
// import { PersonSchema, GameSchema, BreadcrumbSchema } from '@/lib/structured-data'
//
// export default function HomePage() {
// return (
// <>
// <PersonSchema />
// <BreadcrumbSchema path="/" labels={['Home']} />
// {/* ...rest of page */}
// </>
// )
// }
//
// Each component renders a <script type="application/ld+json"> tag.
// Multiple schemas per page are fine and encouraged (Person + Breadcrumb,
// Game + Breadcrumb, Article + Breadcrumb, etc.).
// ---------------------------------------------------------------------------
import { BASE_URL, SITE_TAGLINE } from '@/lib/site/constants'
const AUTHOR = {
'@type': 'Person',
name: 'Jake Lawrence',
url: BASE_URL,
jobTitle: 'Customer Success Engagement Specialist',
sameAs: [
'https://www.linkedin.com/in/jacobalawrence',
'https://github.com/jake0lawrence',
],
}
// ---------------------------------------------------------------------------
// Helper: renders a JSON-LD script tag
// ---------------------------------------------------------------------------
function JsonLd({ data }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
)
}
// ---------------------------------------------------------------------------
// PersonSchema — homepage only
// ---------------------------------------------------------------------------
export function PersonSchema() {
return (
<JsonLd
data={{
'@context': 'https://schema.org',
'@type': 'Person',
name: 'Jake Lawrence',
url: BASE_URL,
jobTitle: 'Customer Success Engagement Specialist',
worksFor: {
'@type': 'Organization',
name: 'TransUnion',
},
alumniOf: {
'@type': 'EducationalOrganization',
name: 'Northern Illinois University',
},
sameAs: [
'https://www.linkedin.com/in/jacobalawrence',
'https://github.com/jake0lawrence',
],
knowsAbout: [
'GovTech',
'Municipal Government',
'LLM Research',
'Civic Infrastructure',
'Educational Game Design',
],
}}
/>
)
}
// ---------------------------------------------------------------------------
// WebsiteSchema — homepage only, enables sitelinks search box in Google
// ---------------------------------------------------------------------------
export function WebsiteSchema() {
return (
<JsonLd
data={{
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Jake Lawrence',
url: BASE_URL,
description: SITE_TAGLINE,
author: AUTHOR,
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: `${BASE_URL}/search?q={search_term_string}`,
},
'query-input': 'required name=search_term_string',
},
}}
/>
)
}
// ---------------------------------------------------------------------------
// GameSchema — math games, AI concept games, other games
// ---------------------------------------------------------------------------
export function GameSchema({
name,
description,
path,
category = 'EducationalApplication',
genre,
}) {
const data = {
'@context': 'https://schema.org',
'@type': 'WebApplication',
name,
description,
url: `${BASE_URL}${path}`,
applicationCategory: category,
operatingSystem: 'Web Browser',
author: AUTHOR,
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'USD',
},
}
if (genre && genre.length > 0) {
data.genre = genre
}
return <JsonLd data={data} />
}
// ---------------------------------------------------------------------------
// ArticleSchema — essays, novels, research papers
// ---------------------------------------------------------------------------
export function ArticleSchema({
headline,
description,
path,
wordCount,
genre,
datePublished,
dateModified,
image,
schemaType = 'Article',
}) {
const data = {
'@context': 'https://schema.org',
'@type': schemaType,
headline,
description,
url: `${BASE_URL}${path}`,
author: AUTHOR,
publisher: AUTHOR,
}
if (wordCount) data.wordCount = wordCount
if (genre && genre.length > 0) data.genre = genre
if (datePublished) data.datePublished = datePublished
if (dateModified) data.dateModified = dateModified
if (image) data.image = image
return <JsonLd data={data} />
}
// ---------------------------------------------------------------------------
// SoftwareSchema — tools (Keymaster, Prompt Mirror, etc.)
// ---------------------------------------------------------------------------
export function SoftwareSchema({
name,
description,
path,
category = 'DeveloperApplication',
technologies,
}) {
const data = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name,
description,
url: `${BASE_URL}${path}`,
applicationCategory: category,
operatingSystem: 'Web Browser',
author: AUTHOR,
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'USD',
},
}
if (technologies && technologies.length > 0) {
data.programmingLanguage = technologies
}
return <JsonLd data={data} />
}
// ---------------------------------------------------------------------------
// ResearchSchema — research projects (SAGEN, LLM-QP)
// ---------------------------------------------------------------------------
export function ResearchSchema({
name,
description,
path,
about,
}) {
const data = {
'@context': 'https://schema.org',
'@type': 'TechArticle',
headline: name,
description,
url: `${BASE_URL}${path}`,
author: AUTHOR,
publisher: AUTHOR,
}
if (about && about.length > 0) {
data.about = about.map((topic) => ({
'@type': 'Thing',
name: topic,
}))
}
return <JsonLd data={data} />
}
// ---------------------------------------------------------------------------
// BreadcrumbSchema — every page should have this
// ---------------------------------------------------------------------------
//
// Usage:
// <BreadcrumbSchema
// path="/research/sagen"
// labels={['Research', 'SAGEN']}
// />
//
// Generates: Home > Research > SAGEN
// Each segment links to the cumulative path.
// ---------------------------------------------------------------------------
export function BreadcrumbSchema({ path, labels }) {
const segments = path.split('/').filter(Boolean)
const items = [
{
'@type': 'ListItem',
position: 1,
name: 'Home',
item: BASE_URL,
},
]
let currentPath = BASE_URL
segments.forEach((segment, i) => {
currentPath += `/${segment}`
const label =
labels?.[i] ||
segment
.replace(/-/g, ' ')
.replace(/\b\w/g, (c) => c.toUpperCase())
items.push({
'@type': 'ListItem',
position: i + 2,
name: label,
item: currentPath,
})
})
return (
<JsonLd
data={{
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items,
}}
/>
)
}
Where it lives
- src/lib/structured-data.jsx
See it in action
Architecture map →FAQ
Why components for JSON-LD?
Each schema is a small JSX component that renders a script tag, so any page can compose several (Article plus Breadcrumb, Software plus Breadcrumb) with no duplication.
Part of these stacks
Related systems
Want a system like this built for you?Work with me →