Skip to content

Commit 628fa4a

Browse files
committed
feat: add dynamic og image
1 parent d48ed83 commit 628fa4a

File tree

9 files changed

+129
-15
lines changed

9 files changed

+129
-15
lines changed

public/images/og.jpg

1.77 KB
Loading

src/app/(home)/page.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// import { Footer, Topbar } from "@/components/docs/layouts";
21
import { Footer } from "@/components/docs/layouts/footer";
32
import { Topbar } from "@/components/docs/layouts/topbar";
43
import { Newsletter } from "@/components/docs/newsletter";
@@ -7,8 +6,6 @@ import { Demo } from "./components/demo";
76
import { Feature } from "./components/feature";
87
import { Hero } from "./components/hero";
98

10-
// import { docsSidebarNavItems } from "@/app/docs/menu";
11-
129
export default function LandingPage() {
1310
return (
1411
<div>

src/app/docs/[[...slug]]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { DocsBreadcrumb } from "@/components/docs/layouts/breadcrumb";
88
import { CopyMarkdown } from "@/components/docs/layouts/copy-markdown";
99
import { Footer } from "@/components/docs/layouts/footer";
1010
import { getMDXComponents } from "@/components/docs/mdx/mdx-components";
11-
import { routes } from "@/lib/docs";
11+
import { getMetadata, routes } from "@/lib/docs";
1212
import { source } from "@/lib/source";
1313

1414
export async function generateStaticParams() {
@@ -20,10 +20,10 @@ export async function generateMetadata(props: PageProps<"/docs/[[...slug]]">): P
2020
const page = source.getPage(params.slug);
2121
if (!page) notFound();
2222

23-
return {
23+
return getMetadata({
2424
title: page.data.title,
2525
description: page.data.description,
26-
};
26+
});
2727
}
2828

2929
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {

src/app/docs/blocks/page.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ import Link from "next/link";
55
import { Footer } from "@/components/docs/layouts/footer";
66
import { Badge } from "@/components/ui/badge";
77
import { Card } from "@/components/ui/card";
8-
import { routes } from "@/lib/docs";
8+
import { getMetadata, routes } from "@/lib/docs";
99

1010
import { blockSections } from "./menu";
1111

12-
export const metadata: Metadata = {
13-
title: "Blocks",
14-
description:
15-
"Beautiful, interactive, and production-ready sections you can drop into your project with a single shadcn command.",
16-
};
17-
12+
export async function generateMetadata(): Promise<Metadata> {
13+
return getMetadata({
14+
title: "Blocks",
15+
description: "Beautiful, interactive, and production-ready sections you can drop into your project with a single shadcn command.",
16+
});
17+
}
1818
export default async function Page() {
1919
return (
2020
<DocsPage

src/app/docs/components/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { DocsBody, DocsPage } from "fumadocs-ui/page";
22
import { ArrowRightIcon } from "lucide-react";
3+
import { Metadata } from "next";
34
import Link from "next/link";
45
import { ReactNode, useMemo } from "react";
56

67
import { Footer } from "@/components/docs/layouts/footer";
7-
import { routes } from "@/lib/docs";
8+
import { getMetadata, routes } from "@/lib/docs";
89
import { source } from "@/lib/source";
910

1011
type Item = {
@@ -13,6 +14,13 @@ type Item = {
1314
items?: Item[];
1415
};
1516

17+
export async function generateMetadata(): Promise<Metadata> {
18+
return getMetadata({
19+
title: "Components",
20+
description: "Beautiful, animated components that respond smoothly, feel natural, and elevate user experience.",
21+
});
22+
}
23+
1624
const Page = () => {
1725
const groupedItems = useMemo(() => {
1826
const pages = source.pageTree;

src/app/fetch-mdx/[[...slug]]/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fs from "node:fs/promises";
44

55
import { source } from "@/lib/source";
66

7-
// export const revalidate = 604800;
7+
export const revalidate = 604800;
88

99
export function generateStaticParams() {
1010
return source.generateParams();

src/app/og/route.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { ImageResponse } from "next/og";
2+
3+
async function loadGoogleFont(font: string, text: string) {
4+
const url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent(text)}`;
5+
const css = await (await fetch(url)).text();
6+
const resource = css.match(
7+
/src: url\((.+)\) format\('(opentype|truetype)'\)/,
8+
);
9+
10+
if (resource) {
11+
const response = await fetch(resource[1]);
12+
if (response.status == 200) {
13+
return await response.arrayBuffer();
14+
}
15+
}
16+
17+
throw new Error('failed to load font data');
18+
}
19+
20+
export async function GET(request: Request) {
21+
const { searchParams } = new URL(request.url);
22+
const title = searchParams.get("title");
23+
const description = searchParams.get("description");
24+
25+
26+
return new ImageResponse(
27+
(
28+
<div tw="flex h-full w-full bg-white text-black" style={{ fontFamily: "Work Sans" }}>
29+
<div tw="border absolute border-neutral-200 border-dashed inset-y-0 left-16 w-[1px]" />
30+
<div tw="border absolute border-neutral-200 border-dashed inset-y-0 right-16 w-[1px]" />
31+
<div tw="border absolute border-neutral-200 inset-x-0 h-[1px] bottom-16" />
32+
<p tw="absolute bottom-2 right-24 text-neutral-600">
33+
@paceui
34+
</p>
35+
<div tw="flex flex-col absolute w-[896px] inset-x-32 inset-y-24">
36+
<img src="https://paceui.com/images/brand/logo-light.svg" tw="h-20" alt="Logo"/>
37+
<div tw="flex flex-col mt-24">
38+
<div
39+
tw="tracking-tight flex-grow-1 flex flex-col justify-center leading-[1.1]"
40+
style={{
41+
textWrap: "balance",
42+
fontSize: title && title.length > 20 ? 64 : 80,
43+
letterSpacing: "-0.04em",
44+
}}>
45+
{title}
46+
</div>
47+
<div
48+
tw="text-[40px] leading-[1.5] flex-grow-1 text-neutral-600 mt-4"
49+
style={{
50+
textWrap: "balance",
51+
}}>
52+
{description}
53+
</div>
54+
</div>
55+
</div>
56+
</div>
57+
),
58+
{
59+
width: 1200,
60+
height: 630,
61+
fonts: [
62+
{
63+
name: 'Work Sans',
64+
data: await loadGoogleFont(
65+
'Work Sans',
66+
`${title} ${description} @paceui`,
67+
),
68+
style: 'normal',
69+
},
70+
],
71+
},
72+
);
73+
}

src/lib/docs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./fetch-file";
22
export * from "./newsletter";
33
export * from "./routes";
4+
export * from "./metadata";

src/lib/docs/metadata.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { Metadata } from "next";
2+
3+
type Props = {
4+
title: string;
5+
description?: string;
6+
};
7+
8+
export const getMetadata = ({ title, description }: Props): Metadata => {
9+
return {
10+
title: title,
11+
description: description,
12+
openGraph: {
13+
title: title,
14+
description: description,
15+
type: "article",
16+
url: "https://ui.paceui.com",
17+
images: [
18+
{
19+
url: `/og?title=${encodeURIComponent(title)}&description=${encodeURIComponent(description ?? "")}`,
20+
},
21+
],
22+
},
23+
twitter: {
24+
card: "summary_large_image",
25+
title: title,
26+
description: description,
27+
images: [
28+
{
29+
url: `/og?title=${encodeURIComponent(title)}&description=${encodeURIComponent(description ?? "")}`,
30+
},
31+
],
32+
creator: "@paceui_",
33+
},
34+
};
35+
};

0 commit comments

Comments
 (0)