Skip to content

Commit 3e0aeb1

Browse files
committed
fix: breadcrumb bad path
1 parent b467505 commit 3e0aeb1

File tree

3 files changed

+96
-43
lines changed

3 files changed

+96
-43
lines changed

app/[lang]/[[...mdxPath]]/page.tsx

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-disable react-hooks/rules-of-hooks -- false positive, useMDXComponents are not react hooks */
22

3-
import { BASE_URL } from '@/app/_constants/project'
43
import { JsonLd, generateArticleSchema } from '@app/_components/json-ld'
54
import { generateBreadcrumbSchema } from '@app/_utils/generate-breadcrumb-schema'
65
import { Metadata } from 'next'
@@ -9,38 +8,49 @@ import { useMDXComponents } from '../../../mdx-components'
98

109
export const generateStaticParams = generateStaticParamsFor('mdxPath')
1110

12-
export async function generateMetadata(props: PageProps): Promise<Metadata | null> {
13-
try {
14-
const params = await props.params
15-
const { metadata } = await importPage(params.mdxPath || [], params.lang)
16-
const isHomepage = !params.mdxPath || params.mdxPath.length === 0
11+
export async function generateMetadata({ params }): Promise<Metadata> {
12+
const { frontMatter, metadata } = await importPage(params.mdxPath || [], params.lang)
13+
const isHomepage = !params.mdxPath || params.mdxPath.length === 0
14+
const baseUrl = 'https://uxpatterns.dev'
15+
const path = params.mdxPath?.join('/') || ''
1716

18-
// Include the language prefix in the canonical path
19-
const canonicalPath = `/${params.lang}${params.mdxPath ? `/${params.mdxPath.join('/')}` : ''}`
17+
// Shared metadata
18+
const title = frontMatter?.title || metadata?.title || 'UX Patterns for Devs'
19+
const description = frontMatter?.description || metadata?.description || ''
20+
const summary = frontMatter?.summary || ''
2021

21-
const ogImage = {
22-
url: isHomepage
23-
? '/og/opengraph-image.png'
24-
: `/api/og?title=${encodeURIComponent(metadata.title || '')}`,
25-
width: 1200,
26-
height: 630,
27-
type: 'image/png',
28-
}
22+
// OG image handling
23+
const isPatternPage = params.mdxPath?.[0] === 'patterns'
24+
const patternName = isPatternPage ? params.mdxPath?.[params.mdxPath.length - 1] : null
25+
const ogImageUrl = isHomepage
26+
? '/og/opengraph-image.png'
27+
: `/api/og?title=${encodeURIComponent(title)}`
2928

30-
return {
31-
...metadata,
32-
openGraph: {
33-
...metadata.openGraph,
34-
images: [ogImage],
35-
url: `${BASE_URL}${canonicalPath}`
36-
},
37-
alternates: {
38-
canonical: `${BASE_URL}${canonicalPath}`
39-
}
40-
}
41-
} catch (e) {
42-
console.error('Error generating metadata:', e)
43-
return null
29+
return {
30+
title,
31+
description,
32+
openGraph: {
33+
title,
34+
description,
35+
type: 'article',
36+
images: [
37+
...(isPatternPage && patternName ? [{
38+
url: `/covers/patterns/${patternName}.png`,
39+
width: 800,
40+
height: 400,
41+
alt: `${summary} - UX Pattern`,
42+
}] : []),
43+
{
44+
url: ogImageUrl,
45+
width: 1200,
46+
height: 630,
47+
alt: description || title,
48+
},
49+
],
50+
},
51+
alternates: {
52+
canonical: `${baseUrl}/${params.lang}${path ? `/${path}` : ''}`,
53+
},
4454
}
4555
}
4656

@@ -55,26 +65,25 @@ const Wrapper = useMDXComponents().wrapper
5565

5666
export default async function Page(props: PageProps) {
5767
const params = await props.params
58-
const result = await importPage(params.mdxPath, params.lang)
59-
const { default: MDXContent, toc, metadata } = result
68+
const { default: MDXContent, toc, metadata } = await importPage(params.mdxPath, params.lang)
6069

61-
// Get the OG image URL from metadata
70+
// Use the same logic for OG image as in generateMetadata
6271
const isHomepage = !params.mdxPath || params.mdxPath.length === 0
72+
const title = metadata?.title || 'UX Patterns for Devs'
73+
const description = metadata?.description || ''
6374
const ogImageUrl = isHomepage
6475
? '/og/opengraph-image.png'
65-
: `/api/og?title=${encodeURIComponent(metadata.title || '')}`
76+
: `/api/og?title=${encodeURIComponent(title)}`
6677

6778
const schemaData = generateArticleSchema(
68-
metadata.title || '',
69-
metadata.description || '',
79+
title,
80+
description,
7081
`/${params.lang}/${params.mdxPath?.join('/') || ''}`,
7182
ogImageUrl
7283
)
7384

74-
// Generate breadcrumb items
75-
const breadcrumbs = [
76-
{ title: 'Home', url: `/${params.lang}` }
77-
]
85+
// Generate breadcrumb items for patterns only
86+
const breadcrumbs: Array<{ title: string; url: string }> = []
7887

7988
if (params.mdxPath) {
8089
let currentPath = ''
@@ -93,7 +102,7 @@ export default async function Page(props: PageProps) {
93102
return (
94103
<>
95104
<JsonLd data={schemaData} />
96-
<JsonLd data={breadcrumbSchema} />
105+
{breadcrumbs.length > 0 && <JsonLd data={breadcrumbSchema} />}
97106
<div className="nextra-content">
98107
<Wrapper key={pageKey} toc={toc} metadata={metadata}>
99108
<MDXContent {...props} params={params} />

app/[lang]/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export default async function RootLayout({ children, params }: {
7777
<html lang={lang} suppressHydrationWarning>
7878
<head>
7979
<PlausibleProvider domain="uxpatterns.dev" trackOutboundLinks={true} taggedEvents={true} />
80+
<script src="https://analytics.ahrefs.com/analytics.js" data-key="kp+2z+UG2C+LV5KT2+/B+w" async></script>
8081
</head>
8182
<body className={`${fontSans.variable} ${fontMono.variable} bg-background font-sans antialiased`}>
8283
<Layout

app/_utils/generate-breadcrumb-schema.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,54 @@ interface BreadcrumbItem {
66
}
77

88
export function generateBreadcrumbSchema(breadcrumbs: BreadcrumbItem[]) {
9+
// Extract language code from the first breadcrumb that has a URL with language
10+
const langMatch = breadcrumbs.find(crumb => crumb.url)?.url?.match(/^\/([a-z]{2})\//)
11+
const lang = langMatch ? langMatch[1] : 'en' // Default to 'en' if no language found
12+
13+
// Ensure home page is always first in the breadcrumb with correct language
14+
const homePage: BreadcrumbItem = {
15+
title: 'Home',
16+
url: `/${lang}`
17+
}
18+
19+
// Filter breadcrumbs based on the section
20+
const filteredBreadcrumbs = breadcrumbs.reduce((acc: BreadcrumbItem[], crumb, index) => {
21+
// Always keep the main section (patterns, pattern-guide, etc.)
22+
if (crumb.url?.match(/^\/[a-z]{2}\/[^/]+$/)) {
23+
acc.push(crumb)
24+
return acc
25+
}
26+
27+
// For the patterns section, skip category level
28+
if (crumb.url?.includes('/patterns/')) {
29+
// If it's the last item, keep it
30+
if (index === breadcrumbs.length - 1) {
31+
acc.push(crumb)
32+
}
33+
return acc
34+
}
35+
36+
// For all other sections, keep everything
37+
acc.push(crumb)
38+
return acc
39+
}, [])
40+
41+
// Combine home page with filtered breadcrumbs
42+
const fullBreadcrumbs = [homePage, ...filteredBreadcrumbs]
43+
44+
if (fullBreadcrumbs.length === 0) {
45+
return {
46+
"@context": "https://schema.org",
47+
"@type": "BreadcrumbList",
48+
"itemListElement": []
49+
}
50+
}
51+
952
return {
1053
"@context": "https://schema.org",
1154
"@type": "BreadcrumbList",
12-
"itemListElement": breadcrumbs.map((crumb, index) => {
13-
const isLastItem = index === breadcrumbs.length - 1
55+
"itemListElement": fullBreadcrumbs.map((crumb, index) => {
56+
const isLastItem = index === fullBreadcrumbs.length - 1
1457
const baseItem = {
1558
"@type": "ListItem",
1659
"position": index + 1,

0 commit comments

Comments
 (0)