From 78e4bd20b77c6d9c617e0f66e21faa0fdf365718 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sat, 31 Oct 2026 19:41:32 -0600 Subject: [PATCH 1/2] Add semantic breadcrumbs to partner detail pages --- src/routes/partners.$partner.tsx | 26 +++++++++++------------ src/utils/partner-breadcrumbs.ts | 22 ++++++++++++++++++++ tests/partner-breadcrumbs.test.ts | 34 +++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 src/utils/partner-breadcrumbs.ts create mode 100644 tests/partner-breadcrumbs.test.ts diff --git a/src/routes/partners.$partner.tsx b/src/routes/partners.$partner.tsx index ef4bf8370..17866bee3 100644 --- a/src/routes/partners.$partner.tsx +++ b/src/routes/partners.$partner.tsx @@ -1,3 +1,8 @@ +import { Breadcrumbs } from '~/components/Breadcrumbs' +import { + getPartnerBreadcrumbItems, + getPartnerBreadcrumbJsonLd, +} from '~/utils/partner-breadcrumbs' import * as React from 'react' import { Link, createFileRoute, notFound } from '@tanstack/react-router' import { @@ -59,7 +64,10 @@ export const Route = createFileRoute('/partners/$partner')({ scripts: [ { type: 'application/ld+json', - children: JSON.stringify(getPartnerJsonLd(partner)), + children: JSON.stringify({ + ...getPartnerJsonLd(partner), + breadcrumb: getPartnerBreadcrumbJsonLd(partner), + }), }, ], } @@ -94,18 +102,10 @@ function PartnerDetailPage() {
-
- - Partners - - / - - {partner.name} - -
+
diff --git a/src/utils/partner-breadcrumbs.ts b/src/utils/partner-breadcrumbs.ts new file mode 100644 index 000000000..019a587f9 --- /dev/null +++ b/src/utils/partner-breadcrumbs.ts @@ -0,0 +1,22 @@ +import { canonicalUrl } from './seo' + +type PartnerBreadcrumbData = { id: string; name: string } + +export function getPartnerBreadcrumbItems(partner: PartnerBreadcrumbData) { + return [ + { label: 'Partners', to: '/partners' }, + { label: partner.name, to: `/partners/${partner.id}` }, + ] +} + +export function getPartnerBreadcrumbJsonLd(partner: PartnerBreadcrumbData) { + return { + '@type': 'BreadcrumbList', + itemListElement: getPartnerBreadcrumbItems(partner).map((item, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: item.label, + item: canonicalUrl(item.to), + })), + } +} diff --git a/tests/partner-breadcrumbs.test.ts b/tests/partner-breadcrumbs.test.ts new file mode 100644 index 000000000..464dbc516 --- /dev/null +++ b/tests/partner-breadcrumbs.test.ts @@ -0,0 +1,34 @@ +import assert from 'node:assert/strict' +import { test } from 'node:test' +import { + getPartnerBreadcrumbItems, + getPartnerBreadcrumbJsonLd, +} from '../src/utils/partner-breadcrumbs' + +Object.assign(globalThis, { __TANSTACK_SITE_URL__: 'https://tanstack.com' }) + +test('partner breadcrumbs share the visible labels and canonical destinations', () => { + const partner = { id: 'ag-grid', name: 'AG Grid' } + const items = getPartnerBreadcrumbItems(partner) + assert.deepEqual(items, [ + { label: 'Partners', to: '/partners' }, + { label: 'AG Grid', to: '/partners/ag-grid' }, + ]) + assert.deepEqual(getPartnerBreadcrumbJsonLd(partner), { + '@type': 'BreadcrumbList', + itemListElement: [ + { + '@type': 'ListItem', + position: 1, + name: 'Partners', + item: 'https://tanstack.com/partners', + }, + { + '@type': 'ListItem', + position: 2, + name: 'AG Grid', + item: 'https://tanstack.com/partners/ag-grid', + }, + ], + }) +}) From e3a628077a7e772fa2e2ea71060b3a1932b880f3 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sat, 31 Oct 2026 19:50:12 -0600 Subject: [PATCH 2/2] Use shared breadcrumbs on bespoke partner pages --- src/routes/partners.netlify.tsx | 26 +++++++++++++++----------- src/routes/partners.railway.tsx | 26 +++++++++++++------------- src/routes/partners.render.tsx | 26 +++++++++++++------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/routes/partners.netlify.tsx b/src/routes/partners.netlify.tsx index 62fe0a273..6941dcfba 100644 --- a/src/routes/partners.netlify.tsx +++ b/src/routes/partners.netlify.tsx @@ -1,3 +1,8 @@ +import { Breadcrumbs } from '~/components/Breadcrumbs' +import { + getPartnerBreadcrumbItems, + getPartnerBreadcrumbJsonLd, +} from '~/utils/partner-breadcrumbs' import * as React from 'react' import { Link, createFileRoute } from '@tanstack/react-router' import { @@ -288,7 +293,10 @@ export const Route = createFileRoute('/partners/netlify')({ ? [ { type: 'application/ld+json', - children: JSON.stringify(getPartnerJsonLd(partner)), + children: JSON.stringify({ + ...getPartnerJsonLd(partner), + breadcrumb: getPartnerBreadcrumbJsonLd(partner), + }), }, ] : []), @@ -350,16 +358,12 @@ function NetlifyPartnerPage() { return (
- + {/* Hero */}
diff --git a/src/routes/partners.railway.tsx b/src/routes/partners.railway.tsx index 132044f61..4cf250998 100644 --- a/src/routes/partners.railway.tsx +++ b/src/routes/partners.railway.tsx @@ -1,3 +1,8 @@ +import { Breadcrumbs } from '~/components/Breadcrumbs' +import { + getPartnerBreadcrumbItems, + getPartnerBreadcrumbJsonLd, +} from '~/utils/partner-breadcrumbs' import * as React from 'react' import { Link, createFileRoute } from '@tanstack/react-router' import { @@ -265,7 +270,10 @@ export const Route = createFileRoute('/partners/railway')({ scripts: [ { type: 'application/ld+json', - children: JSON.stringify(getPartnerJsonLd(RAILWAY_PARTNER)), + children: JSON.stringify({ + ...getPartnerJsonLd(RAILWAY_PARTNER), + breadcrumb: getPartnerBreadcrumbJsonLd(RAILWAY_PARTNER), + }), }, { type: 'application/ld+json', @@ -333,18 +341,10 @@ function RailwayPartnerPage() { return (
- + {/* Hero */}
diff --git a/src/routes/partners.render.tsx b/src/routes/partners.render.tsx index 019900422..96707bfb1 100644 --- a/src/routes/partners.render.tsx +++ b/src/routes/partners.render.tsx @@ -1,3 +1,8 @@ +import { Breadcrumbs } from '~/components/Breadcrumbs' +import { + getPartnerBreadcrumbItems, + getPartnerBreadcrumbJsonLd, +} from '~/utils/partner-breadcrumbs' import * as React from 'react' import { Link, createFileRoute } from '@tanstack/react-router' import { @@ -273,7 +278,10 @@ export const Route = createFileRoute('/partners/render')({ scripts: [ { type: 'application/ld+json', - children: JSON.stringify(getPartnerJsonLd(RENDER_PARTNER)), + children: JSON.stringify({ + ...getPartnerJsonLd(RENDER_PARTNER), + breadcrumb: getPartnerBreadcrumbJsonLd(RENDER_PARTNER), + }), }, { type: 'application/ld+json', @@ -341,18 +349,10 @@ function RenderPartnerPage() { return (
- + {/* Hero */}