Second Post
This is the second post
/app/posts/[id]/page.tsx
import { Suspense } from "react";
import { Metadata } from "next";
import { Code } from "@/components/code";
import { Row } from "@/components/row";
import { getPost, getPosts } from "@/lib/actions";
// Surround Post Component with Suspense to show a loading state
export default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
return (
<Suspense fallback={<div>Loading post...</div>}>
<Post id={id} />
</Suspense>
);
}
export async function generateStaticParams() {
const { data: posts } = await getPosts();
if (!posts) return [];
return posts.map((post) => ({ id: post.id.toString() }));
}
// The component that fetches and renders the post
async function Post({ id }: { id: string }) {
const post = await getPost(id);
if (!post) {
return <div>Post not found</div>;
}
return (
<Row>
<div className="flex flex-col gap-4">
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
<div>
<Code path="/app/posts/[id]/page.tsx" />
<Code path="/lib/actions.ts" sub={[38, 43]} />
<Code path="/app/posts/[id]/opengraph-image.tsx" />
</div>
</Row>
);
}
// Metadata for SEO
export async function generateMetadata({
params,
}: {
params: Promise<{ id: string }>;
}): Promise<Metadata> {
const { id } = await params;
const post = await getPost(id);
return {
title: post?.title,
};
}
/lib/actions.ts (lines 39-44)
interface ActionResponse<T> {
data?: T;
error?: Error;
}
/app/posts/[id]/opengraph-image.tsx
import { getPost } from "@/lib/actions";
import { ImageResponse } from "next/og";
// Image metadata
export const size = {
width: 1200,
height: 630,
};
export const contentType = "image/png";
// Image generation
export default async function Image({ params }: { params: { id: string } }) {
const { id } = params;
const post = await getPost(id);
return new ImageResponse(
(
<div
style={{
height: "100%",
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#fff",
fontSize: 32,
fontWeight: 600,
}}
>
<svg
width="75"
viewBox="0 0 75 65"
fill="#000"
style={{ margin: "0 75px" }}
>
<path d="M37.59.25l36.95 64H.64l36.95-64z"></path>
</svg>
<div style={{ marginTop: 40 }}>{post?.title}</div>
</div>
)
);
}