diff --git a/app/[locale]/blog/[slug]/opengraph-image.tsx b/app/[locale]/blog/[slug]/opengraph-image.tsx new file mode 100644 index 00000000..74fcb887 --- /dev/null +++ b/app/[locale]/blog/[slug]/opengraph-image.tsx @@ -0,0 +1,113 @@ +import { ImageResponse } from 'next/og'; +import { getPostBySlug } from '@/lib/blog'; + +export const runtime = 'edge'; + +export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug: string } }) { + const post = await getPostBySlug(slug, locale); + + if (!post) { + return new ImageResponse( +
+ ); + } + + return new ImageResponse( + ( +
+ {/* Background Image Overlay if available */} + {post.frontmatter.featuredImage && ( + + )} + + {/* Gradient Overlay */} +
+ +
+ {post.frontmatter.category && ( +
+ {post.frontmatter.category} +
+ )} +
+ {post.frontmatter.title} +
+
+
+ KLZ Cables Blog +
+
+
+ {new Date(post.frontmatter.date).toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric' })} +
+
+
+
+ ), + { + width: 1200, + height: 630, + } + ); +} diff --git a/app/[locale]/blog/[slug]/page.tsx b/app/[locale]/blog/[slug]/page.tsx index bb328e68..7e1fb4f8 100644 --- a/app/[locale]/blog/[slug]/page.tsx +++ b/app/[locale]/blog/[slug]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from 'next/navigation'; import { MDXRemote } from 'next-mdx-remote/rsc'; import { getPostBySlug, getAdjacentPosts } from '@/lib/blog'; +import { Metadata } from 'next'; interface BlogPostProps { params: { @@ -9,6 +10,32 @@ interface BlogPostProps { }; } +export async function generateMetadata({ params: { locale, slug } }: BlogPostProps): Promise { + const post = await getPostBySlug(slug, locale); + + if (!post) return {}; + + const description = post.frontmatter.excerpt || ''; + + return { + title: post.frontmatter.title, + description: description, + openGraph: { + title: post.frontmatter.title, + description: description, + type: 'article', + publishedTime: post.frontmatter.date, + authors: ['KLZ Cables'], + url: `https://klz-cables.com/${locale}/blog/${slug}`, + }, + twitter: { + card: 'summary_large_image', + title: post.frontmatter.title, + description: description, + }, + }; +} + import Link from 'next/link'; import VisualLinkPreview from '@/components/blog/VisualLinkPreview'; import Callout from '@/components/blog/Callout'; @@ -253,6 +280,34 @@ export default async function BlogPost({ params: { locale, slug } }: BlogPostPro
+ {/* Structured Data */} +