Skip to content

Commit 8a3228e

Browse files
ericyangpanclaude
andcommitted
feat(seo): add OpenGraph image templates for all routes
Add OpenGraph image generation for better social media sharing previews across all routes. Changes: - Add opengraph-image.tsx files for all main routes: - Home page, Articles (list + detail), CLIs (list + detail + comparison) - Extensions (list + detail + comparison), IDEs (list + detail + comparison) - Models (list + detail + comparison), Providers (list + detail) - Vendors (list + detail) - Create reusable OGImageTemplate component (src/components/og/OGImageTemplate.tsx) - All OG images follow 1200x630px standard and global minimalist design system These file-based OG images are auto-detected by Next.js without manual metadata configuration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2363fe9 commit 8a3228e

File tree

16 files changed

+518
-0
lines changed

16 files changed

+518
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ImageResponse } from 'next/og'
2+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
3+
import { getArticle } from '@/lib/data/fetchers'
4+
5+
export const size = { width: 1200, height: 630 }
6+
export const contentType = 'image/png'
7+
8+
interface Props {
9+
params: Promise<{ locale: string; slug: string }>
10+
}
11+
12+
export default async function Image({ params }: Props) {
13+
const { locale, slug } = await params
14+
const article = await getArticle(slug, locale)
15+
16+
if (!article) {
17+
return new ImageResponse(
18+
<OGImageTemplate
19+
category="ARTICLE"
20+
title="AI Coding Stack"
21+
description="Insights and guides for AI-powered development"
22+
/>,
23+
{ ...size }
24+
)
25+
}
26+
27+
return new ImageResponse(
28+
<OGImageTemplate category="ARTICLE" title={article.title} description={article.description} />,
29+
{ ...size }
30+
)
31+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ImageResponse } from 'next/og'
2+
import { getTranslations } from 'next-intl/server'
3+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
4+
5+
export const size = { width: 1200, height: 630 }
6+
export const contentType = 'image/png'
7+
8+
interface Props {
9+
params: Promise<{ locale: string }>
10+
}
11+
12+
export default async function Image({ params }: Props) {
13+
const { locale } = await params
14+
const t = await getTranslations({ locale, namespace: 'components.header' })
15+
16+
return new ImageResponse(
17+
<OGImageTemplate
18+
category="ARTICLES"
19+
title={t('articles')}
20+
description="Insights, guides, and best practices for AI-powered development"
21+
/>,
22+
{ ...size }
23+
)
24+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ImageResponse } from 'next/og'
2+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
3+
import type { Locale } from '@/i18n/config'
4+
import { getCLI } from '@/lib/data/fetchers'
5+
6+
export const size = { width: 1200, height: 630 }
7+
export const contentType = 'image/png'
8+
9+
interface Props {
10+
params: Promise<{ locale: string; slug: string }>
11+
}
12+
13+
export default async function Image({ params }: Props) {
14+
const { locale, slug } = await params
15+
const cli = await getCLI(slug, locale as Locale)
16+
17+
if (!cli) {
18+
return new ImageResponse(
19+
<OGImageTemplate
20+
category="AI CODING ASSISTANT CLI"
21+
title="AI Coding Stack"
22+
description="Discover the best AI coding CLI tools for developers"
23+
/>,
24+
{ ...size }
25+
)
26+
}
27+
28+
return new ImageResponse(
29+
<OGImageTemplate
30+
category="AI CODING ASSISTANT CLI"
31+
title={cli.name}
32+
description={cli.description}
33+
vendor={cli.vendor}
34+
/>,
35+
{ ...size }
36+
)
37+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ImageResponse } from 'next/og'
2+
import { getTranslations } from 'next-intl/server'
3+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
4+
5+
export const size = { width: 1200, height: 630 }
6+
export const contentType = 'image/png'
7+
8+
interface Props {
9+
params: Promise<{ locale: string }>
10+
}
11+
12+
export default async function Image({ params }: Props) {
13+
const { locale } = await params
14+
const t = await getTranslations({ locale, namespace: 'pages.clis' })
15+
16+
return new ImageResponse(
17+
<OGImageTemplate
18+
category="AI CODING ASSISTANT CLIS"
19+
title={t('title')}
20+
description={t('subtitle')}
21+
/>,
22+
{ ...size }
23+
)
24+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ImageResponse } from 'next/og'
2+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
3+
import type { Locale } from '@/i18n/config'
4+
import { getExtension } from '@/lib/data/fetchers'
5+
6+
export const size = { width: 1200, height: 630 }
7+
export const contentType = 'image/png'
8+
9+
interface Props {
10+
params: Promise<{ locale: string; slug: string }>
11+
}
12+
13+
export default async function Image({ params }: Props) {
14+
const { locale, slug } = await params
15+
const extension = await getExtension(slug, locale as Locale)
16+
17+
if (!extension) {
18+
return new ImageResponse(
19+
<OGImageTemplate
20+
category="AI CODING EXTENSION"
21+
title="AI Coding Stack"
22+
description="Discover the best AI coding extensions for your IDE"
23+
/>,
24+
{ ...size }
25+
)
26+
}
27+
28+
return new ImageResponse(
29+
<OGImageTemplate
30+
category="AI CODING EXTENSION"
31+
title={extension.name}
32+
description={extension.description}
33+
vendor={extension.vendor}
34+
/>,
35+
{ ...size }
36+
)
37+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ImageResponse } from 'next/og'
2+
import { getTranslations } from 'next-intl/server'
3+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
4+
5+
export const size = { width: 1200, height: 630 }
6+
export const contentType = 'image/png'
7+
8+
interface Props {
9+
params: Promise<{ locale: string }>
10+
}
11+
12+
export default async function Image({ params }: Props) {
13+
const { locale } = await params
14+
const t = await getTranslations({ locale, namespace: 'pages.extensions' })
15+
16+
return new ImageResponse(
17+
<OGImageTemplate
18+
category="AI CODING EXTENSIONS"
19+
title={t('title')}
20+
description={t('subtitle')}
21+
/>,
22+
{ ...size }
23+
)
24+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ImageResponse } from 'next/og'
2+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
3+
import type { Locale } from '@/i18n/config'
4+
import { getIDE } from '@/lib/data/fetchers'
5+
6+
export const size = { width: 1200, height: 630 }
7+
export const contentType = 'image/png'
8+
9+
interface Props {
10+
params: Promise<{ locale: string; slug: string }>
11+
}
12+
13+
export default async function Image({ params }: Props) {
14+
const { locale, slug } = await params
15+
const ide = await getIDE(slug, locale as Locale)
16+
17+
if (!ide) {
18+
return new ImageResponse(
19+
<OGImageTemplate
20+
category="AI-POWERED IDE"
21+
title="AI Coding Stack"
22+
description="Discover the best AI coding tools and IDEs for developers"
23+
/>,
24+
{ ...size }
25+
)
26+
}
27+
28+
return new ImageResponse(
29+
<OGImageTemplate
30+
category="AI-POWERED IDE"
31+
title={ide.name}
32+
description={ide.description}
33+
vendor={ide.vendor}
34+
/>,
35+
{ ...size }
36+
)
37+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ImageResponse } from 'next/og'
2+
import { getTranslations } from 'next-intl/server'
3+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
4+
5+
export const size = { width: 1200, height: 630 }
6+
export const contentType = 'image/png'
7+
8+
interface Props {
9+
params: Promise<{ locale: string }>
10+
}
11+
12+
export default async function Image({ params }: Props) {
13+
const { locale } = await params
14+
const t = await getTranslations({ locale, namespace: 'pages.ides' })
15+
16+
return new ImageResponse(
17+
<OGImageTemplate category="AI-POWERED IDES" title={t('title')} description={t('subtitle')} />,
18+
{ ...size }
19+
)
20+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ImageResponse } from 'next/og'
2+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
3+
import type { Locale } from '@/i18n/config'
4+
import { getModelProvider } from '@/lib/data/fetchers'
5+
6+
export const size = { width: 1200, height: 630 }
7+
export const contentType = 'image/png'
8+
9+
interface Props {
10+
params: Promise<{ locale: string; slug: string }>
11+
}
12+
13+
export default async function Image({ params }: Props) {
14+
const { locale, slug } = await params
15+
const provider = await getModelProvider(slug, locale as Locale)
16+
17+
if (!provider) {
18+
return new ImageResponse(
19+
<OGImageTemplate
20+
category="AI MODEL PROVIDER"
21+
title="AI Coding Stack"
22+
description="Discover leading AI model providers and platforms"
23+
/>,
24+
{ ...size }
25+
)
26+
}
27+
28+
return new ImageResponse(
29+
<OGImageTemplate
30+
category="AI MODEL PROVIDER"
31+
title={provider.name}
32+
description={provider.description}
33+
/>,
34+
{ ...size }
35+
)
36+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ImageResponse } from 'next/og'
2+
import { getTranslations } from 'next-intl/server'
3+
import { OGImageTemplate } from '@/components/og/OGImageTemplate'
4+
5+
export const size = { width: 1200, height: 630 }
6+
export const contentType = 'image/png'
7+
8+
interface Props {
9+
params: Promise<{ locale: string }>
10+
}
11+
12+
export default async function Image({ params }: Props) {
13+
const { locale } = await params
14+
const t = await getTranslations({ locale, namespace: 'pages.modelProviders' })
15+
16+
return new ImageResponse(
17+
<OGImageTemplate
18+
category="AI MODEL PROVIDERS"
19+
title={t('title')}
20+
description={t('subtitle')}
21+
/>,
22+
{ ...size }
23+
)
24+
}

0 commit comments

Comments
 (0)