mirror of
https://github.com/supabase/supabase.git
synced 2026-06-16 02:26:42 +08:00
## 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`.
96 lines
3.7 KiB
TypeScript
96 lines
3.7 KiB
TypeScript
import 'swiper/css'
|
|
|
|
import DefaultLayout from '~/components/Layouts/Default'
|
|
import ModulesNav from '~/components/Modules/ModulesNav'
|
|
import vectorPageData from '~/data/products/modules/vector'
|
|
import { useBreakpoint } from 'common'
|
|
import { NextSeo } from 'next-seo'
|
|
import dynamic from 'next/dynamic'
|
|
import Head from 'next/head'
|
|
import { PRODUCT_MODULES_NAMES, PRODUCT_MODULES_SHORTNAMES } from 'shared-data/products'
|
|
|
|
import { breadcrumbs } from '@/lib/breadcrumbs'
|
|
import { breadcrumbListSchema, serializeJsonLd, softwareApplicationSchema } from '@/lib/json-ld'
|
|
|
|
const ProductModulesHeader = dynamic(() => import('~/components/Sections/ProductModulesHeader'))
|
|
const HighlightCards = dynamic(() => import('~/components/Sections/HighlightCards'))
|
|
const FeaturesSection = dynamic(() => import('~/components/Sections/FeaturesSection'))
|
|
const UseCasesSection = dynamic(() => import('~/components/Sections/UseCasesSection'))
|
|
const CenteredTitleImage = dynamic(() => import('~/components/Sections/CenteredTitleImage'))
|
|
const CustomerQuotesSection = dynamic(() => import('~/components/Sections/CustomerQuotesSection'))
|
|
const TimedTabsSection = dynamic(() => import('~/components/Sections/TimedTabsSection'))
|
|
const ProductsCta = dynamic(() => import('~/components/Sections/ProductsCta'))
|
|
const EnterpriseCta = dynamic(() => import('~/components/Sections/EnterpriseCta'))
|
|
|
|
// When updating page content, also update public/llms/vector.txt
|
|
|
|
function VectorPage() {
|
|
// base path for images
|
|
const isXs = useBreakpoint(640)
|
|
const pageData = vectorPageData(isXs)
|
|
const meta_title = pageData.metaTitle
|
|
const meta_description = pageData.metaDescription
|
|
const meta_image = pageData.metaImage
|
|
|
|
return (
|
|
<>
|
|
<NextSeo
|
|
title={meta_title}
|
|
description={meta_description}
|
|
openGraph={{
|
|
title: meta_title,
|
|
description: meta_description,
|
|
url: `https://supabase.com/modules/vector`,
|
|
images: [
|
|
{
|
|
url: meta_image,
|
|
},
|
|
],
|
|
}}
|
|
/>
|
|
<Head>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{
|
|
__html: serializeJsonLd(
|
|
softwareApplicationSchema({
|
|
name: 'Supabase Vector',
|
|
description: meta_description,
|
|
url: 'https://supabase.com/modules/vector',
|
|
image: meta_image,
|
|
})
|
|
),
|
|
}}
|
|
/>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{
|
|
__html: serializeJsonLd(breadcrumbListSchema(breadcrumbs.vector)),
|
|
}}
|
|
/>
|
|
</Head>
|
|
<DefaultLayout className="bg-alternative!" stickyNavbar={false}>
|
|
<ModulesNav activePage={PRODUCT_MODULES_NAMES.VECTOR} docsUrl={pageData.docsUrl} />
|
|
<ProductModulesHeader {...pageData.heroSection} />
|
|
<HighlightCards {...(pageData.highlightsSection as any)} />
|
|
<CenteredTitleImage {...pageData.integrations} />
|
|
<TimedTabsSection {...pageData.APIsection} />
|
|
<div className="bg-alternative">
|
|
<UseCasesSection {...pageData.useCasesSection} />
|
|
<FeaturesSection {...pageData.featuresSection} />
|
|
</div>
|
|
<CustomerQuotesSection {...pageData.quotesSection} />
|
|
<div className="bg-linear-to-t from-alternative to-transparent">
|
|
<EnterpriseCta />
|
|
</div>
|
|
<div className="bg-background">
|
|
<div className="w-full h-px bg-linear-to-r from-background-alternative via-border to-background-alternative" />
|
|
<ProductsCta currentProduct={PRODUCT_MODULES_SHORTNAMES.VECTOR} />
|
|
</div>
|
|
</DefaultLayout>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default VectorPage
|