diff --git a/app/devices/[slug]/opengraph-image.tsx b/app/devices/[slug]/opengraph-image.tsx
new file mode 100644
index 0000000..4f9c7ba
--- /dev/null
+++ b/app/devices/[slug]/opengraph-image.tsx
@@ -0,0 +1,120 @@
+import { ImageResponse } from "next/og"
+import { db } from "@/lib/db/index"
+import { hardware, performanceEntries, gameVersions, games } from "@/lib/db/schema"
+import { eq, and, sql } from "drizzle-orm"
+
+export const runtime = "edge"
+export const alt = "Device benchmarks on DeckyVault"
+export const size = { width: 1200, height: 630 }
+export const contentType = "image/png"
+
+export default async function Image({
+ params,
+}: {
+ params: Promise<{ slug: string }>
+}) {
+ const { slug } = await params
+
+ const [device] = await db
+ .select({
+ name: hardware.name,
+ deviceType: hardware.deviceType,
+ })
+ .from(hardware)
+ .where(eq(hardware.slug, slug))
+ .limit(1)
+
+ if (!device) {
+ return new ImageResponse(
+ (
+
+
DeckyVault
+
+ Device Not Found
+
+
+ ),
+ { ...size }
+ )
+ }
+
+ const [stats] = await db
+ .select({
+ totalBenchmarks: sql`count(*)::int`,
+ avgFps: sql`round(avg(${performanceEntries.fpsAvg})::numeric, 1)`,
+ gameCount: sql`count(distinct ${gameVersions.gameId})::int`,
+ })
+ .from(performanceEntries)
+ .innerJoin(gameVersions, eq(performanceEntries.versionId, gameVersions.id))
+ .innerJoin(games, eq(gameVersions.gameId, games.id))
+ .where(
+ and(
+ eq(performanceEntries.hardwareSlug, slug),
+ eq(performanceEntries.isRemoved, false)
+ )
+ )
+
+ const typeLabel = device.deviceType === "handheld" ? "Handheld" : "Console"
+ const avgFpsStr = stats?.avgFps ? String(Math.round(Number(stats.avgFps))) : "—"
+
+ return new ImageResponse(
+ (
+
+
+
+ {device.name}
+
+
+
+ {stats?.totalBenchmarks ?? 0} Benchmarks
+
+
+ {avgFpsStr} Avg FPS
+
+
+ {stats?.gameCount ?? 0} Games
+
+
+
+ deckyvault.xyz
+
+
+ ),
+ { ...size }
+ )
+}
diff --git a/app/sitemap.ts b/app/sitemap.ts
index 58f7e41..7d5ae60 100644
--- a/app/sitemap.ts
+++ b/app/sitemap.ts
@@ -26,7 +26,6 @@ export default async function sitemap(): Promise {
return [
{ url: BASE_URL, lastModified: new Date(), changeFrequency: "weekly", priority: 1 },
- { url: `${BASE_URL}/search`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.7 },
{ url: `${BASE_URL}/devices`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 },
...gameEntries,
...deviceEntries,