diff --git a/apps/web/app/about/opengraph-image.tsx b/apps/web/app/about/opengraph-image.tsx
new file mode 100644
index 0000000..8fe70ec
--- /dev/null
+++ b/apps/web/app/about/opengraph-image.tsx
@@ -0,0 +1,23 @@
+import { ImageResponse } from "next/og";
+import { OGImageTemplate } from "../../src/components/OGImageTemplate";
+import { getOgFonts, OG_IMAGE_SIZE } from "../../src/lib/og-helper";
+
+export const size = OG_IMAGE_SIZE;
+export const contentType = "image/png";
+export const runtime = "nodejs";
+
+export default async function Image() {
+ const fonts = await getOgFonts();
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_IMAGE_SIZE,
+ fonts: fonts as any,
+ },
+ );
+}
diff --git a/apps/web/app/api/og/[[...slug]]/route.tsx b/apps/web/app/api/og/[[...slug]]/route.tsx
deleted file mode 100644
index 8279bc6..0000000
--- a/apps/web/app/api/og/[[...slug]]/route.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { ImageResponse } from "next/og";
-import { blogPosts } from "../../../../src/data/blogPosts";
-import { blogThumbnails } from "../../../../src/data/blogThumbnails";
-import { OGImageTemplate } from "../../../../src/components/OGImageTemplate";
-import { getOgFonts, OG_IMAGE_SIZE } from "../../../../src/lib/og-helper";
-
-export const runtime = "nodejs";
-
-export async function GET(
- request: Request,
- { params }: { params: Promise<{ slug?: string[] }> },
-) {
- const { slug: slugArray } = await params;
- const slug = slugArray?.[0] || "home";
-
- let title: string;
- let description: string;
- let label: string | undefined;
- let accentColor: string | undefined;
- let keyword: string | undefined;
-
- if (slug === "home") {
- title = "Marc Mintel";
- description =
- "Technical problem solver's blog - practical insights and learning notes";
- label = "Engineering";
- } else {
- const post = blogPosts.find((p) => p.slug === slug);
- const thumbnail = blogThumbnails[slug];
- title = post?.title || "Marc Mintel";
- description =
- post?.description ||
- "Technical problem solver's blog - practical insights and learning notes";
- label = post ? "Blog Post" : "Engineering";
- accentColor = thumbnail?.accent;
- keyword = thumbnail?.keyword;
- }
-
- const fonts = await getOgFonts();
-
- return new ImageResponse(
- ,
- {
- ...OG_IMAGE_SIZE,
- fonts: fonts as any,
- },
- );
-}
diff --git a/apps/web/app/blog/[slug]/opengraph-image.tsx b/apps/web/app/blog/[slug]/opengraph-image.tsx
new file mode 100644
index 0000000..6cd959d
--- /dev/null
+++ b/apps/web/app/blog/[slug]/opengraph-image.tsx
@@ -0,0 +1,43 @@
+import { ImageResponse } from "next/og";
+import { blogPosts } from "../../../src/data/blogPosts";
+import { blogThumbnails } from "../../../src/data/blogThumbnails";
+import { OGImageTemplate } from "../../../src/components/OGImageTemplate";
+import { getOgFonts, OG_IMAGE_SIZE } from "../../../src/lib/og-helper";
+
+export const size = OG_IMAGE_SIZE;
+export const contentType = "image/png";
+export const runtime = "nodejs";
+
+export default async function Image({
+ params,
+}: {
+ params: Promise<{ slug: string }>;
+}) {
+ const { slug } = await params;
+ const post = blogPosts.find((p) => p.slug === slug);
+ const thumbnail = blogThumbnails[slug];
+
+ const title = post?.title || "Marc Mintel";
+ const description =
+ post?.description ||
+ "Technical problem solver's blog - practical insights and learning notes";
+ const label = post ? "Blog Post" : "Engineering";
+ const accentColor = thumbnail?.accent;
+ const keyword = thumbnail?.keyword;
+
+ const fonts = await getOgFonts();
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_IMAGE_SIZE,
+ fonts: fonts as any,
+ },
+ );
+}
diff --git a/apps/web/app/blog/[slug]/page.tsx b/apps/web/app/blog/[slug]/page.tsx
index 7436b9e..c9cb5d2 100644
--- a/apps/web/app/blog/[slug]/page.tsx
+++ b/apps/web/app/blog/[slug]/page.tsx
@@ -33,20 +33,11 @@ export async function generateMetadata({
title: post.title,
description: post.description,
type: "article",
- images: [
- {
- url: `/api/og/${slug}`,
- width: 1200,
- height: 630,
- alt: post.title,
- },
- ],
},
twitter: {
card: "summary_large_image",
title: post.title,
description: post.description,
- images: [`/api/og/${slug}`],
},
};
}
diff --git a/apps/web/app/contact/opengraph-image.tsx b/apps/web/app/contact/opengraph-image.tsx
new file mode 100644
index 0000000..ab9509f
--- /dev/null
+++ b/apps/web/app/contact/opengraph-image.tsx
@@ -0,0 +1,23 @@
+import { ImageResponse } from "next/og";
+import { OGImageTemplate } from "../../src/components/OGImageTemplate";
+import { getOgFonts, OG_IMAGE_SIZE } from "../../src/lib/og-helper";
+
+export const size = OG_IMAGE_SIZE;
+export const contentType = "image/png";
+export const runtime = "nodejs";
+
+export default async function Image() {
+ const fonts = await getOgFonts();
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_IMAGE_SIZE,
+ fonts: fonts as any,
+ },
+ );
+}
diff --git a/apps/web/app/opengraph-image.tsx b/apps/web/app/opengraph-image.tsx
new file mode 100644
index 0000000..8e247e8
--- /dev/null
+++ b/apps/web/app/opengraph-image.tsx
@@ -0,0 +1,23 @@
+import { ImageResponse } from "next/og";
+import { OGImageTemplate } from "../src/components/OGImageTemplate";
+import { getOgFonts, OG_IMAGE_SIZE } from "../src/lib/og-helper";
+
+export const size = OG_IMAGE_SIZE;
+export const contentType = "image/png";
+export const runtime = "nodejs";
+
+export default async function Image() {
+ const fonts = await getOgFonts();
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_IMAGE_SIZE,
+ fonts: fonts as any,
+ },
+ );
+}
diff --git a/apps/web/src/lib/og-helper.tsx b/apps/web/src/lib/og-helper.tsx
index 284ddc9..2bd1ab7 100644
--- a/apps/web/src/lib/og-helper.tsx
+++ b/apps/web/src/lib/og-helper.tsx
@@ -6,14 +6,36 @@ import { join } from "path";
* Since we are using runtime = 'nodejs', we can read them from the filesystem.
*/
export async function getOgFonts() {
- const boldFontPath = join(
- process.cwd(),
- "apps/web/public/fonts/Inter-Bold.woff",
- );
- const regularFontPath = join(
- process.cwd(),
- "apps/web/public/fonts/Inter-Regular.woff",
- );
+ // Use relative path from process.cwd() which usually is the monorepo root or apps/web
+ // We try both to be safe during different execution contexts (dev vs build vs runtime)
+ const pathsToTry = [
+ join(process.cwd(), "apps/web/public/fonts"),
+ join(process.cwd(), "public/fonts"),
+ ];
+
+ let boldFontPath = "";
+ let regularFontPath = "";
+
+ for (const basePath of pathsToTry) {
+ const b = join(basePath, "Inter-Bold.woff");
+ const r = join(basePath, "Inter-Regular.woff");
+ try {
+ if (readFileSync(b) && readFileSync(r)) {
+ boldFontPath = b;
+ regularFontPath = r;
+ break;
+ }
+ } catch (e) {
+ continue;
+ }
+ }
+
+ if (!boldFontPath || !regularFontPath) {
+ console.error(
+ `[OG] Could not find fonts in either ${pathsToTry.join(" or ")}`,
+ );
+ return [];
+ }
try {
console.log(
@@ -40,7 +62,7 @@ export async function getOgFonts() {
},
];
} catch (error) {
- console.error(`[OG] Failed to load fonts from ${process.cwd()}:`, error);
+ console.error(`[OG] Failed to load fonts:`, error);
return [];
}
}