Files
supabase/apps/www/pages/modules/cron.tsx
Pamela Chia c26b64a033 feat(www): emit BreadcrumbList JSON-LD on marketing surfaces (#45478)
## Summary

- Adds `breadcrumbListSchema(items)` helper to `apps/www/lib/json-ld.ts`
and a hand-curated `apps/www/lib/breadcrumbs.ts` route map.
- Wires inline `<script type="application/ld+json">` BreadcrumbList
blocks into 18 marketing surfaces: blog (index + slug), customers (index
+ slug), events (index + slug), 5 product pages (database, auth,
storage, edge-functions, realtime), 3 modules (vector, cron, queues),
pricing, careers, company, features.
- Pages router callers wrap the script in `<Head>`; app router callers
place it directly in JSX. Dynamic surfaces append a leaf at render time
using the page's title (`frontmatter.title` for blog, `meta_title ??
title` for customers, `event.meta_title ?? event.title` for events).
- Modules sit at `Home > {Name}` since no `/modules` index page exists;
products sit at `Home > {Product}` (no shared products parent). Absolute
`https://supabase.com` URLs match the existing `CANONICAL_ORIGIN`
convention so anchors stay stable across Vercel previews.

Linear:
[GROWTH-822](https://linear.app/supabase/issue/GROWTH-822/add-breadcrumblist-json-ld-to-www-marketing-surfaces)
(sub-issue under
[GROWTH-724](https://linear.app/supabase/issue/GROWTH-724)).

> **Note on branch name:** the branch is
`pamela/growth-820-www-breadcrumb-jsonld`; the actual Linear issue is
GROWTH-822. The branch was named before the sub-issue was created.
Ignore the `820` in the branch.

Explicitly deferred (separate PRs / low SEO ROI): `/launch-week/*`,
`/solutions/*`, `/partners/*`, `/alternatives/*`, `/changelog`,
`/legal/dpa`, `/aws-reinvent-2025`, `/wrapped`, `/contribute/*`,
`/brand-assets`, `/ga`, `/ga-week`, `/state-of-startups*`, and the
homepage (Organization + WebSite already cover homepage entity signals;
single-item BreadcrumbList is ignored by Google).

## Test plan

- [x] On the Vercel preview, `curl -s https://<preview>/database | grep
'"BreadcrumbList"'` returns the script block with `Home > Database`.
- [x] `curl -s https://<preview>/blog/<recent-slug> | grep
'"BreadcrumbList"'` returns `Home > Blog > {post title}`.
- [x] `curl -s https://<preview>/customers/<slug> | grep
'"BreadcrumbList"'` returns `Home > Customer Stories > {customer
title}`.
- [x] `curl -s https://<preview>/events/<slug> | grep
'"BreadcrumbList"'` returns `Home > Events > {event title}`.
- [x] `curl -s https://<preview>/modules/vector | grep
'"BreadcrumbList"'` returns `Home > Vector`.
2026-05-05 23:57:53 +08:00

63 lines
2.3 KiB
TypeScript

import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import ModulesNav from '~/components/Modules/ModulesNav'
import ProductModulesHeader from '~/components/Sections/ProductModulesHeader'
import CronPageData from '~/data/products/modules/cron'
import { breadcrumbs } from '~/lib/breadcrumbs'
import { breadcrumbListSchema, serializeJsonLd } from '~/lib/json-ld'
import { NextSeo } from 'next-seo'
import dynamic from 'next/dynamic'
import Head from 'next/head'
import { PRODUCT_MODULES_NAMES } from 'shared-data/products'
const HighlightCards = dynamic(() => import('~/components/Sections/HighlightCards'))
const CronSQLSection = dynamic(() => import('~/components/Modules/Cron/CronSQLSection'))
const ImageParagraphSection = dynamic(() => import('~/components/Sections/ImageParagraphSection'))
const CTABanner = dynamic(() => import('~/components/CTABanner'))
function CronPage() {
const pageData = CronPageData()
return (
<>
<NextSeo
title={pageData.metaTitle}
description={pageData.metaDescription}
openGraph={{
title: pageData.metaTitle,
description: pageData.metaDescription,
url: `https://supabase.com/modules/cron`,
images: [
{
url: pageData.metaImage,
},
],
}}
/>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.cron)),
}}
/>
</Head>
<DefaultLayout className="bg-alternative!" stickyNavbar={false}>
<ModulesNav activePage={PRODUCT_MODULES_NAMES.CRON} docsUrl={pageData.docsUrl} />
<ProductModulesHeader {...pageData.heroSection} />
<SectionContainer>{pageData.videoSection.video}</SectionContainer>
<HighlightCards {...(pageData.highlightsSection as any)} />
<CronSQLSection {...pageData.section1} />
<ImageParagraphSection {...pageData.section2} />
<ImageParagraphSection {...pageData.section3} />
<ImageParagraphSection {...pageData.section4} />
<div className="bg-linear-to-t from-alternative to-transparent mt-8 lg:mt-24">
<CTABanner />
</div>
</DefaultLayout>
</>
)
}
export default CronPage