Skip to content

Commit 0d63d73

Browse files
committed
fix: og:img
1 parent 1b27910 commit 0d63d73

File tree

10 files changed

+628
-60
lines changed

10 files changed

+628
-60
lines changed

.DS_Store

0 Bytes
Binary file not shown.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { ImageResponse } from "next/og";
2+
// Updated import path to match the working page.tsx file
3+
import { getBlogPosts } from "@/blogs/utils";
4+
5+
// Route segment config
6+
export const runtime = "nodejs";
7+
8+
// Image metadata
9+
export const alt = "Sumit So - Blog Post"; // More specific alt text
10+
export const size = { width: 1200, height: 630 };
11+
export const contentType = "image/png";
12+
13+
// Image generation
14+
export default async function Image({ params }: { params: { slug: string } }) {
15+
// 1. Find the specific blog post using the slug from the URL
16+
const post = getBlogPosts().find((p) => p.slug === params.slug);
17+
18+
// 2. Handle the case where the post is not found
19+
if (!post) {
20+
return new Response(
21+
`Failed to generate OG Image: Post not found for slug: ${params.slug}`,
22+
{
23+
status: 404,
24+
}
25+
);
26+
}
27+
28+
// 3. Extract metadata for the specific post
29+
const { title, publishedAt } = post.metadata; // Assuming author might be in metadata
30+
const formattedDate = new Date(publishedAt).toLocaleDateString("en-US", {
31+
month: "long",
32+
day: "numeric",
33+
year: "numeric",
34+
});
35+
36+
// Author image URL
37+
const baseUrl = "https://www.sumitso.in";
38+
const authorImageUrl = `${baseUrl}/sumit-home.png`;
39+
40+
return new ImageResponse(
41+
(
42+
// Root container - redesigned for a single post
43+
<div
44+
style={{
45+
display: "flex",
46+
flexDirection: "column",
47+
alignItems: "center",
48+
gap: "80px",
49+
justifyContent: "center",
50+
// justifyContent: "space-between", // Pushes content apart
51+
height: "100%",
52+
width: "100%",
53+
fontFamily: '"Inter"', // Make sure to load this font if you switch to edge runtime
54+
backgroundColor: "#f6f5f1",
55+
padding: "60px",
56+
textAlign: "center",
57+
}}
58+
>
59+
{/* Top Branding */}
60+
<div
61+
style={{
62+
display: "flex",
63+
fontSize: 28,
64+
fontWeight: 700,
65+
color: "#353534",
66+
}}
67+
>
68+
SUMITSO.IN/<span style={{ color: "#009688" }}>BLOGS</span>
69+
</div>
70+
71+
{/* Main Content: Post Title */}
72+
<div
73+
style={{
74+
display: "flex",
75+
flexDirection: "column",
76+
alignItems: "center",
77+
gap: "24px",
78+
}}
79+
>
80+
<h1
81+
style={{
82+
fontSize: 64,
83+
fontWeight: 700,
84+
color: "#1a1a1a",
85+
lineHeight: 1.2,
86+
margin: 0,
87+
padding: "0 20px", // Add some horizontal padding
88+
}}
89+
>
90+
{title}
91+
</h1>
92+
</div>
93+
94+
{/* Bottom Author Info */}
95+
<div
96+
style={{
97+
display: "flex",
98+
alignItems: "center",
99+
justifyContent: "center",
100+
gap: "20px",
101+
}}
102+
>
103+
<img
104+
src={authorImageUrl}
105+
width="60"
106+
height="60"
107+
style={{
108+
borderRadius: "100%",
109+
}}
110+
/>
111+
<div
112+
style={{
113+
display: "flex",
114+
flexDirection: "column",
115+
alignItems: "flex-start",
116+
}}
117+
>
118+
<div style={{ fontSize: 24, fontWeight: 600, color: "#353534" }}>
119+
{"Sumit So"}
120+
</div>
121+
<div style={{ fontSize: 20, color: "#6b7280" }}>
122+
{formattedDate}
123+
</div>
124+
</div>
125+
</div>
126+
</div>
127+
),
128+
{
129+
...size,
130+
// If you were using the edge runtime, you would need to fetch and provide fonts here.
131+
// With the nodejs runtime, it has better system font support.
132+
}
133+
);
134+
}

app/blogs/[slug]/page.tsx

Lines changed: 135 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,119 @@
1+
// import { notFound } from "next/navigation";
2+
// import { CustomMDX } from "@/components/mdx";
3+
// import { getBlogPosts } from "@/blogs/utils";
4+
// import { formatDate } from "@/lib/utils";
5+
// import { baseUrl } from "@/sitemap";
6+
// import Footer from "@/components/footer";
7+
// import Header from "@/components/header";
8+
// import Link from "next/link";
9+
// import { canela_th } from "@/lib/fonts";
10+
11+
// export async function generateStaticParams() {
12+
// let posts = getBlogPosts();
13+
14+
// return posts.map((post) => ({
15+
// slug: post.slug,
16+
// }));
17+
// }
18+
19+
// export function generateMetadata({ params }) {
20+
// let post = getBlogPosts().find((post) => post.slug === params.slug);
21+
// if (!post) {
22+
// return;
23+
// }
24+
25+
// let {
26+
// title,
27+
// publishedAt: publishedTime,
28+
// summary: description,
29+
// image,
30+
// } = post.metadata;
31+
// let ogImage = image ? image : `/blogs/${params.slug}/opengraph-image.png`;
32+
33+
// return {
34+
// title,
35+
// description,
36+
// openGraph: {
37+
// title,
38+
// description,
39+
// type: "article",
40+
// locale: "en_US",
41+
// publishedTime,
42+
// url: `/blogs/${post.slug}`,
43+
// images: [
44+
// {
45+
// url: ogImage,
46+
// width: 1200,
47+
// height: 630,
48+
// alt: "Sumit So - Blog Post",
49+
// },
50+
// ],
51+
// },
52+
// twitter: {
53+
// card: "summary_large_image",
54+
// title,
55+
// description,
56+
// images: [ogImage],
57+
// },
58+
// };
59+
// }
60+
61+
// export default function Blog({ params }) {
62+
// let post = getBlogPosts().find((post) => post.slug === params.slug);
63+
64+
// if (!post) {
65+
// notFound();
66+
// }
67+
68+
// return (
69+
// <>
70+
// <Header />
71+
// <section className="mx-auto text-left py-6 px-6 sm:px-8 md:px-10 lg:px-12 lg:w-[70%] md:w-[80%] sm:w-[90%] w-[100%]">
72+
// <Link
73+
// href={"/blogs"}
74+
// className={`relative mb-6 flex w-[100px] items-center text-tealBright ${canela_th.className} text-2xl hover:text-black hover:pl-6 transition-all duration-200 after:content-[''] after:absolute after:left-0 after:transform after:-translate-x-full after:opacity-0 hover:after:opacity-100 hover:after:translate-x-0 after:transition-all after:duration-200 after:w-4 after:h-4 after:bg-no-repeat after:bg-left after:bg-contain after:bg-[url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20fill%3D%27none%27%20viewBox%3D%270%200%2024%2024%27%20stroke%3D%27teal%27%20class%3D%27w-4%20h-4%27%3E%3Cpath%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%20stroke-width%3D%272%27%20d%3D%27M19%2012H5m7-7l-7%207%207%207%27/%3E%3C/svg%3E")]`}
75+
// >
76+
// Blogs
77+
// </Link>
78+
// <script
79+
// type="application/ld+json"
80+
// suppressHydrationWarning
81+
// dangerouslySetInnerHTML={{
82+
// __html: JSON.stringify({
83+
// "@context": "https://schema.org",
84+
// "@type": "BlogPosting",
85+
// headline: post.metadata.title,
86+
// datePublished: post.metadata.publishedAt,
87+
// dateModified: post.metadata.publishedAt,
88+
// description: post.metadata.summary,
89+
// image: post.metadata.image
90+
// ? `${baseUrl}${post.metadata.image}`
91+
// : `${baseUrl}/blogs/${post.slug}/opengraph-image.png`,
92+
// url: `${baseUrl}/blogs/${post.slug}`,
93+
// author: {
94+
// "@type": "Person",
95+
// name: "My Portfolio",
96+
// },
97+
// }),
98+
// }}
99+
// />
100+
// <h1 className="title font-semibold text-2xl tracking-tighter">
101+
// {post.metadata.title}
102+
// </h1>
103+
// <div className="flex justify-between items-center mt-2 mb-8 text-sm">
104+
// <p className="text-sm text-neutral-600 dark:text-neutral-400">
105+
// {formatDate(post.metadata.publishedAt)}
106+
// </p>
107+
// </div>
108+
// <article className="prose prose-lg prose-img:rounded-lg prose-headings:font-semibold prose-a:text-tealBright hover:prose-a:underline mx-auto max-w-none">
109+
// <CustomMDX source={post.content} />
110+
// </article>
111+
// <Footer />
112+
// </section>
113+
// </>
114+
// );
115+
// }
116+
1117
import { notFound } from "next/navigation";
2118
import { CustomMDX } from "@/components/mdx";
3119
import { getBlogPosts } from "@/blogs/utils";
@@ -17,20 +133,23 @@ export async function generateStaticParams() {
17133
}
18134

19135
export function generateMetadata({ params }) {
20-
let post = getBlogPosts().find((post) => post.slug === params.slug);
136+
const post = getBlogPosts().find((post) => post.slug === params.slug);
21137
if (!post) {
22138
return;
23139
}
24140

25-
let {
141+
const {
26142
title,
27143
publishedAt: publishedTime,
28144
summary: description,
29145
image,
30146
} = post.metadata;
31-
let ogImage = image
32-
? image
33-
: `${baseUrl}/og?title=${encodeURIComponent(title)}`;
147+
148+
// --- THIS IS THE PRIMARY FIX ---
149+
// Determine the image path, then prepend the `baseUrl` to make it absolute.
150+
const imagePath = image ? image : `/blogs/${params.slug}/opengraph-image.png`;
151+
const ogImage = `${baseUrl}${imagePath}`;
152+
// --- END FIX ---
34153

35154
return {
36155
title,
@@ -39,25 +158,29 @@ export function generateMetadata({ params }) {
39158
title,
40159
description,
41160
type: "article",
161+
locale: "en_US",
42162
publishedTime,
43-
url: `${baseUrl}/blog/${post.slug}`,
163+
url: `${baseUrl}/blogs/${post.slug}`, // Use absolute URL for the page
44164
images: [
45165
{
46-
url: ogImage,
166+
url: ogImage, // Correctly uses the absolute URL
167+
width: 1200,
168+
height: 630,
169+
alt: title, // Use the post title for better alt text
47170
},
48171
],
49172
},
50173
twitter: {
51174
card: "summary_large_image",
52175
title,
53176
description,
54-
images: [ogImage],
177+
images: [ogImage], // Also use the absolute URL for Twitter
55178
},
56179
};
57180
}
58181

59182
export default function Blog({ params }) {
60-
let post = getBlogPosts().find((post) => post.slug === params.slug);
183+
const post = getBlogPosts().find((post) => post.slug === params.slug);
61184

62185
if (!post) {
63186
notFound();
@@ -86,11 +209,11 @@ export default function Blog({ params }) {
86209
description: post.metadata.summary,
87210
image: post.metadata.image
88211
? `${baseUrl}${post.metadata.image}`
89-
: `/og?title=${encodeURIComponent(post.metadata.title)}`,
90-
url: `${baseUrl}/blog/${post.slug}`,
212+
: `${baseUrl}/blogs/${post.slug}/opengraph-image.png`,
213+
url: `${baseUrl}/blogs/${post.slug}`,
91214
author: {
92215
"@type": "Person",
93-
name: "My Portfolio",
216+
name: "Sumit So", // Changed from "My Portfolio"
94217
},
95218
}),
96219
}}

app/blogs/layout.tsx

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,43 @@ import { Metadata } from "next";
44

55
export const metadata: Metadata = {
66
title: "Sumit So | Blogs",
7-
description: "Discover stories, thinking, and expertise.",
7+
description:
8+
"Discover stories, thinking, and expertise from Sumit So - Developer Advocate at VideoSDK.live. Explore tutorials, insights, and technical deep-dives on React, WebAssembly, and modern web development.",
9+
openGraph: {
10+
title: "Sumit So | Blogs",
11+
description:
12+
"Discover stories, thinking, and expertise from Sumit So - Developer Advocate at VideoSDK.live. Explore tutorials, insights, and technical deep-dives on React, WebAssembly, and modern web development.",
13+
type: "website",
14+
locale: "en_US",
15+
siteName: "Sumit So",
16+
url: "/blogs",
17+
},
18+
twitter: {
19+
card: "summary_large_image",
20+
title: "Sumit So | Blogs",
21+
description:
22+
"Discover stories, thinking, and expertise from Sumit So - Developer Advocate at VideoSDK.live. Explore tutorials, insights, and technical deep-dives on React, WebAssembly, and modern web development.",
23+
},
24+
keywords: [
25+
"Sumit So",
26+
"Developer Advocate",
27+
"VideoSDK",
28+
"React",
29+
"WebAssembly",
30+
"JavaScript",
31+
"TypeScript",
32+
"Web Development",
33+
"Programming",
34+
"Technical Tutorials",
35+
"Blog",
36+
"Tech Articles",
37+
],
38+
authors: [{ name: "Sumit So", url: "https://sumitso.in" }],
39+
creator: "Sumit So",
40+
publisher: "Sumit So",
41+
alternates: {
42+
canonical: "/blogs",
43+
},
844
};
945

1046
export default function RootLayout({

0 commit comments

Comments
 (0)