From 9ffd87eb0d05e0ff596ee57be20628af55f93ecf Mon Sep 17 00:00:00 2001 From: Bindhu Date: Mon, 1 Jun 2026 11:54:47 +0530 Subject: [PATCH] feat: add dynamic OG meta tags for public profile pages --- src/app/api/og/user/route.tsx | 78 +++++++++++++++++++++++++++++++++++ src/app/u/[username]/page.tsx | 40 ++++++++++++++---- 2 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 src/app/api/og/user/route.tsx diff --git a/src/app/api/og/user/route.tsx b/src/app/api/og/user/route.tsx new file mode 100644 index 00000000..8e010bd7 --- /dev/null +++ b/src/app/api/og/user/route.tsx @@ -0,0 +1,78 @@ +import { ImageResponse } from "@vercel/og"; +import { NextRequest } from "next/server"; + +export const runtime = "edge"; + +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url); + + const username = searchParams.get("username") ?? "developer"; + const name = searchParams.get("name") ?? username; + const avatar = searchParams.get("avatar") ?? ""; + const topLang = searchParams.get("topLang") ?? "JavaScript"; + const streak = searchParams.get("streak") ?? "0"; + const commits = searchParams.get("commits") ?? "0"; + + return new ImageResponse( + ( +
+
+
+
+ +
+ +
+ {avatar ? ( + + ) : ( +
+ {username[0]?.toUpperCase()} +
+ )} +
+ {name} + @{username} +
+
+ +
+ +
+
+ 🔥 Streak + {streak} days +
+
+ 📦 Commits + {Number(commits).toLocaleString()} +
+
+ âš¡ Top Language + {topLang} +
+
+
+ +
+ DevTrack + · devtrack.app/u/{username} +
+
+ ), + { width: 1200, height: 630 } + ); +} \ No newline at end of file diff --git a/src/app/u/[username]/page.tsx b/src/app/u/[username]/page.tsx index 2a653574..18edb672 100644 --- a/src/app/u/[username]/page.tsx +++ b/src/app/u/[username]/page.tsx @@ -45,7 +45,6 @@ export async function generateMetadata({ params: Promise<{ username: string }>; }): Promise { const { username } = await params; - // Minimal lookup — avoids duplicating 3 GitHub API calls that the page already makes const user = await getUserByUsername(username); const profileUrl = getProfileUrl(username); @@ -56,24 +55,49 @@ export async function generateMetadata({ }; } + const baseUrl = + process.env.NEXT_PUBLIC_APP_URL || + process.env.NEXTAUTH_URL || + "http://localhost:3000"; + + // Build dynamic OG image URL + const ogImageUrl = new URL(`${baseUrl}/api/og/user`); +ogImageUrl.searchParams.set("username", username); +ogImageUrl.searchParams.set("name", username); +ogImageUrl.searchParams.set("avatar", `https://avatars.githubusercontent.com/${username}`); +ogImageUrl.searchParams.set("topLang", "Code"); +ogImageUrl.searchParams.set("streak", "0"); +ogImageUrl.searchParams.set("commits", "0"); + + const title = `${username}'s DevTrack Profile`; + const description = `GitHub stats and coding activity for ${username}. View commits, streaks, and top repositories.`; + return { - title: `${username}'s DevTrack Profile`, - description: `GitHub stats and coding activity for ${username}. View commits, streaks, and top repositories.`, + title, + description, openGraph: { - title: `${username}'s DevTrack Profile`, - description: `GitHub stats and coding activity for ${username}`, + title, + description, url: profileUrl, siteName: "DevTrack", type: "profile", + images: [ + { + url: ogImageUrl.toString(), + width: 1200, + height: 630, + alt: `${username}'s DevTrack profile`, + }, + ], }, twitter: { card: "summary_large_image", - title: `${username}'s DevTrack Profile`, - description: `GitHub stats and coding activity for ${username}`, + title, + description, + images: [ogImageUrl.toString()], }, }; } - export default async function PublicProfilePage({ params, }: {