← The Store

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

OG & Metadata ToolkitOne helper that gives every page consistent SEO and social cards.Bauhaus Token SystemThe site-wide design language: CSS variables + JS tokens, dark-aware.Email Identity RegistryPer-surface "From" lines so no brand leaks onto another.

Explore the full catalog →

Want a system like this built for you?Work with me →