Skip to content

Commit 0bd2764

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

File tree

11 files changed

+764
-132
lines changed

11 files changed

+764
-132
lines changed

.DS_Store

0 Bytes
Binary file not shown.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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+
try {
16+
// 1. Find the specific blog post using the slug from the URL
17+
const allPosts = getBlogPosts();
18+
console.log(`Looking for slug: "${params.slug}"`);
19+
console.log(
20+
"Available posts:",
21+
allPosts.map((p) => ({ slug: p.slug, title: p.metadata.title }))
22+
);
23+
24+
const post = allPosts.find((p) => p.slug === params.slug);
25+
26+
// 2. Handle the case where the post is not found
27+
if (!post) {
28+
return new Response(
29+
`Failed to generate OG Image: Post not found for slug: ${
30+
params.slug
31+
}. Available slugs: ${allPosts.map((p) => p.slug).join(", ")}`,
32+
{
33+
status: 404,
34+
}
35+
);
36+
}
37+
38+
// 3. Extract metadata for the specific post
39+
const { title, publishedAt } = post.metadata;
40+
41+
// Add validation for required fields
42+
if (!title || !publishedAt) {
43+
return new Response(
44+
`Missing required metadata: title=${title}, publishedAt=${publishedAt}`,
45+
{
46+
status: 400,
47+
}
48+
);
49+
}
50+
51+
const formattedDate = new Date(publishedAt).toLocaleDateString("en-US", {
52+
month: "long",
53+
day: "numeric",
54+
year: "numeric",
55+
});
56+
57+
// Author image URL
58+
const baseUrl = "https://www.sumitso.in";
59+
const authorImageUrl = `${baseUrl}/sumit-home.png`;
60+
61+
return new ImageResponse(
62+
(
63+
// Root container - redesigned for a single post
64+
<div
65+
style={{
66+
display: "flex",
67+
flexDirection: "column",
68+
alignItems: "center",
69+
gap: "80px",
70+
justifyContent: "center",
71+
// justifyContent: "space-between", // Pushes content apart
72+
height: "100%",
73+
width: "100%",
74+
fontFamily: '"Inter"', // Make sure to load this font if you switch to edge runtime
75+
backgroundColor: "#f6f5f1",
76+
padding: "60px",
77+
textAlign: "center",
78+
}}
79+
>
80+
{/* Top Branding */}
81+
<div
82+
style={{
83+
display: "flex",
84+
fontSize: 28,
85+
fontWeight: 700,
86+
color: "#353534",
87+
}}
88+
>
89+
SUMITSO.IN/<span style={{ color: "#009688" }}>BLOGS</span>
90+
</div>
91+
92+
{/* Main Content: Post Title */}
93+
<div
94+
style={{
95+
display: "flex",
96+
flexDirection: "column",
97+
alignItems: "center",
98+
gap: "24px",
99+
}}
100+
>
101+
<h1
102+
style={{
103+
fontSize: 64,
104+
fontWeight: 700,
105+
color: "#1a1a1a",
106+
lineHeight: 1.2,
107+
margin: 0,
108+
padding: "0 20px", // Add some horizontal padding
109+
}}
110+
>
111+
{title}
112+
</h1>
113+
</div>
114+
115+
{/* Bottom Author Info */}
116+
<div
117+
style={{
118+
display: "flex",
119+
alignItems: "center",
120+
justifyContent: "center",
121+
gap: "20px",
122+
}}
123+
>
124+
<img
125+
src={authorImageUrl}
126+
width="60"
127+
height="60"
128+
style={{
129+
borderRadius: "100%",
130+
}}
131+
/>
132+
<div
133+
style={{
134+
display: "flex",
135+
flexDirection: "column",
136+
alignItems: "flex-start",
137+
}}
138+
>
139+
<div style={{ fontSize: 24, fontWeight: 600, color: "#353534" }}>
140+
{"Sumit So"}
141+
</div>
142+
<div style={{ fontSize: 20, color: "#6b7280" }}>
143+
{formattedDate}
144+
</div>
145+
</div>
146+
</div>
147+
</div>
148+
),
149+
{
150+
...size,
151+
// If you were using the edge runtime, you would need to fetch and provide fonts here.
152+
// With the nodejs runtime, it has better system font support.
153+
}
154+
);
155+
} catch (error: any) {
156+
console.error("Error generating OG image:", error);
157+
return new Response(
158+
`Failed to generate OG Image: ${error?.message || "Unknown error"}`,
159+
{
160+
status: 500,
161+
}
162+
);
163+
}
164+
}

app/blogs/[slug]/page.tsx

Lines changed: 133 additions & 14 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,21 @@ 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+
// Always use the dynamically generated OpenGraph image
149+
// This ensures we get the custom-designed OG image for each blog post
150+
const ogImage = `${baseUrl}/blogs/${params.slug}/opengraph-image.png`;
34151

35152
return {
36153
title,
@@ -39,25 +156,29 @@ export function generateMetadata({ params }) {
39156
title,
40157
description,
41158
type: "article",
159+
locale: "en_US",
42160
publishedTime,
43-
url: `${baseUrl}/blog/${post.slug}`,
161+
url: `${baseUrl}/blogs/${post.slug}`, // Use absolute URL for the page
44162
images: [
45163
{
46-
url: ogImage,
164+
url: ogImage, // Correctly uses the absolute URL
165+
width: 1200,
166+
height: 630,
167+
alt: title, // Use the post title for better alt text
47168
},
48169
],
49170
},
50171
twitter: {
51172
card: "summary_large_image",
52173
title,
53174
description,
54-
images: [ogImage],
175+
images: [ogImage], // Also use the absolute URL for Twitter
55176
},
56177
};
57178
}
58179

59180
export default function Blog({ params }) {
60-
let post = getBlogPosts().find((post) => post.slug === params.slug);
181+
const post = getBlogPosts().find((post) => post.slug === params.slug);
61182

62183
if (!post) {
63184
notFound();
@@ -84,13 +205,11 @@ export default function Blog({ params }) {
84205
datePublished: post.metadata.publishedAt,
85206
dateModified: post.metadata.publishedAt,
86207
description: post.metadata.summary,
87-
image: post.metadata.image
88-
? `${baseUrl}${post.metadata.image}`
89-
: `/og?title=${encodeURIComponent(post.metadata.title)}`,
90-
url: `${baseUrl}/blog/${post.slug}`,
208+
image: `${baseUrl}/blogs/${post.slug}/opengraph-image.png`,
209+
url: `${baseUrl}/blogs/${post.slug}`,
91210
author: {
92211
"@type": "Person",
93-
name: "My Portfolio",
212+
name: "Sumit So", // Changed from "My Portfolio"
94213
},
95214
}),
96215
}}

0 commit comments

Comments
 (0)