diff --git a/bun.lockb b/bun.lockb index 6b9bebd..14f8c8d 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/config.json b/config.json index 683ad11..0b1df09 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,6 @@ { "siteName": "Pulse App", + "ogApiUrl": "https://docs.pulseapp.cc/api/og?title={title}", "metadata": { "title": { "default": "Pulse Docs", diff --git a/package.json b/package.json index e3587a5..290b918 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.3", + "@vercel/og": "^0.6.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "1.0.0", diff --git a/src/app/[[...slug]]/page.tsx b/src/app/[[...slug]]/page.tsx index c59b55f..490f145 100644 --- a/src/app/[[...slug]]/page.tsx +++ b/src/app/[[...slug]]/page.tsx @@ -14,6 +14,7 @@ import { Metadata } from "next"; import Embed from "@/components/embed"; import DocsFooter from "@/components/docs-footer"; import OnThisPage from "@/components/on-this-page"; +import config from "@/config"; /** * The page to render the documentation markdown content. @@ -110,6 +111,7 @@ export const generateMetadata = async ({ return Embed({ title: content.title, description: content.summary, + thumbnail: config.ogApiUrl.replace("{title}", content.title), }); } } diff --git a/src/app/api/og/route.tsx b/src/app/api/og/route.tsx new file mode 100644 index 0000000..bca623b --- /dev/null +++ b/src/app/api/og/route.tsx @@ -0,0 +1,30 @@ +import { ImageResponse } from "next/og"; +import config from "@/config"; + +export const GET = async (request: Request) => { + const { searchParams } = new URL(request.url); + const title: string | undefined = searchParams.has("title") + ? searchParams.get("title")?.slice(0, 100) + : "Hello World (:"; + + return new ImageResponse( + ( +
+ {/* Logo */} + {`${config.siteName} + + {/* Title */} +

{title}

+
+ ), + { + width: 1200, + height: 630, + } + ); +}; diff --git a/src/app/types/config.ts b/src/app/types/config.ts index ff0f4c1..6ea67dc 100644 --- a/src/app/types/config.ts +++ b/src/app/types/config.ts @@ -6,6 +6,11 @@ export type Config = { */ siteName: string; + /** + * The URL to use for generating OG images. + */ + ogApiUrl: string; + /** * The metadata for this app. */ diff --git a/src/components/embed.tsx b/src/components/embed.tsx index 75684e7..12f6249 100644 --- a/src/components/embed.tsx +++ b/src/components/embed.tsx @@ -13,6 +13,11 @@ type EmbedProps = { * The description of the embed. */ description: string; + + /** + * The optional thumbnail image of the embed. + */ + thumbnail?: string | undefined; }; /** @@ -21,13 +26,25 @@ type EmbedProps = { * @param props the embed props * @returns the embed jsx */ -const Embed = ({ title, description }: EmbedProps): Metadata => { +const Embed = ({ title, description, thumbnail }: EmbedProps): Metadata => { return { title: title, openGraph: { title: `${title}`, description: description, + ...(thumbnail && { + images: [ + { + url: thumbnail, + }, + ], + }), }, + ...(thumbnail && { + twitter: { + card: "summary_large_image", + }, + }), }; }; export default Embed;