Hey guys ๐๐๐
In this article, I will write how to generate dynamic Open Graph images with Next.js and TypeScript on Vercel.
For example, the OGI of https://ogp-sample.vercel.app/hoge is like below. (Sorry that I developed this app in Japanese, but you will see the OGI has this site's path name: 'hoge'.)
If you change the path to 'fuga' like https://ogp-sample.vercel.app/fuga, the OGI is like below.
You will see the OGI is automatically generated by the path name of URL.
Now, let's try to generate dynamic OGI!
โป This article is the sixth week of trying to write at least one article every week.
Past articles are listed below.
- Face detection with TensorflowใReact + TypeScriptใ
- UI Components website Released!
- I made 18 UI components for all developers
- Image Transformation: Convert pictures to add styles from famous paintings
- Developed an app to transcribe and translate from images
Setup
First, let's create a template for Next.js + TypeScript.
npx create-next-app --ts
Then, import canvas because we will use canvas to generate the image for OGI.
yarn add canvas@2.6.1
โป Because it seems that canvas@2.7.0 or later does not support node14, here I use version 2.6.1. (If you need, read this comment.)
The structure should be as follows.
fonts/
pages/
ใโ api/
ใโใโ ogi.ts
ใโ [id]/
ใโใโ index.tsx
ใโ index.tsx
Generate the image for OGI
The first step is to create the image for OGI.
I've heard that the best size for OGI is 1200*630, then set the size 1200*630.
(There is a high possibility of garbled Japanese characters and I loaded NotoSansJP in the fonts folder, but if you use only English, you don't need to load the font. It's depends.)
// api/ogi.ts
const createOgi = async (
req: NextApiRequest,
res: NextApiResponse
): Promise<void> => {
const { id } = req.query;
const WIDTH = 1200 as const;
const HEIGHT = 630 as const;
const canvas = createCanvas(WIDTH, HEIGHT);
const ctx = canvas.getContext("2d");
registerFont(path.resolve("./fonts/NotoSansJP-Regular.otf"), {
family: "Noto",
});
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.font = "60px ipagp";
ctx.fillStyle = "#000000";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const text = "ๅ
ฅๅใใๆๅญใฏ" + String(id) + "ใชใฎใญใ";
ctx.fillText(text, WIDTH / 2, HEIGHT / 2);
const buffer = canvas.toBuffer();
res.writeHead(200, {
"Content-Type": "image/png",
"Content-Length": buffer.length,
});
res.end(buffer, "binary");
}
export default createOgi;
yarn dev
and look at http://localhost:3000/api/ogi?id=hoge. If the image is created like below, succeeded.
Rewrite meta
Now let's rewrite the meta information.
With Next.js, we can use <Head> tags
easily.
Since this is a dynamic OGI, we'll use SSR to handle the id and pass it as props.
// pages/[id]/index.tsx
import React from "react";
import { GetServerSidePropsContext, GetServerSidePropsResult } from "next";
import Head from "next/head";
type Props = {
id: string;
};
export const getServerSideProps = async (
context: GetServerSidePropsContext
): Promise<GetServerSidePropsResult<Props>> => {
if (typeof context.params?.id === "string") {
return {
props: {
id: context.params?.id,
},
};
} else {
return {
notFound: true,
};
}
};
const Page = ({ id }: Props) => {
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "";
return (
<>
<Head>
<meta
property="og:image"
key="ogImage"
content={`${baseUrl}/api/ogi?id=${id}`}
/>
<meta
name="twitter:card"
key="twitterCard"
content="summary_large_image"
/>
<meta
name="twitter:image"
key="twitterImage"
content={`${baseUrl}/api/ogi?id=${id}`}
/>
</Head>
<div>
<h1>ๅ
ฅๅใใๆๅญ: {id}</h1>
</div>
</>
);
};
export default Page;
When you visit http://localhost:3000/hoge, the image from http://localhost:3000/api/ogi?id=hoge will be placed as the OGI.
Deploy on Vercel
Now we can just deploy it on Vercel, but if deploy it normally, canvas does not work.
As written in the issue, it seems that we need to yum install when deploy.
So, change the build command.
// package.json
...
"scripts": {
"dev": "next dev",
"build": "next build",
"now-build": "yum install libuuid-devel libmount-devel && cp /lib64/{libuuid,libmount,libblkid}.so.1 node_modules/canvas/build/Release/ && yarn build",
"start": "next start",
"deploy": "now"
},
...
In vercel, if you write "now-build" and specify "deploy": "now", it will build referring to the now-build command instead of default build command.
So, now the build will be done by yarn now-build
instead of yarn build
.
Also, don't forget to set the URL as NEXT_PUBLIC_BASE_URL since we have set NEXT_PUBLIC_BASE_URL as an environment variable.
By the way, here's the one I deployed this time โ https://ogp-sample.vercel.app/
If necessary, you can check it with the share debugger or post it somewhere.
You should now have a dynamic OGI like this.
References
- https://zenn.dev/tiwu_dev/articles/68d58d4ab710af
- https://nextjs.org/docs/basic-features/data-fetching
- https://github.com/vercel/vercel/issues/3460
- https://github.com/Automattic/node-canvas/issues/1779
๐๐๐๐๐๐๐๐
Thank you for reading!
I would be really grad if you give me any feedback!
๐๐๐๐๐๐
Please send me a message if you need.
yuiko.dev@gmail.com
https://twitter.com/yui_active
๐๐๐๐๐๐
Top comments (0)