TailwindCSS Open Graph Images on the Edge
Dec 18, 2022
Kacey Cleveland
Introduction
Vercel introduced a new package a couple of months ago @vercel/og; Using said package when deploying to Vercel, we can generate preview images for blog posts or other types of content that is populated when sharing on social media. This was previously more flexible with a previous implementation using headless chrome and rendering a react generated page as an image to be used as a open graph image. This became increasingly more difficult to use as headless chrome and the other dependencies on top of it could not run on Vercel edge functions due to size constraints on lambda functions or because of missing internal dependencies. This new package from Vercel, while having its own limitations, it makes it a bit more straight forward to get a simple open graph image generated.
What is Open Graph?
Open Graph is a protocol that enables developers to integrate their pages into the social graph. It was introduced by Facebook in 2010 and has since become the de facto standard for integrating web pages into the social media ecosystem.
The Open Graph protocol defines a number of properties that can be used to describe a page, including its title, type, image, and URL. When a page with Open Graph tags is shared on a social media platform, the platform can use this information to create a rich preview of the page, including a thumbnail image, a title, and a brief description. This makes it easier for people to share and discover new content on the web.
Implementation
The implementation is pretty straight forward with NextJS; create a new folder in pages/api to use as your endpoint for rendering the open graph image. In my case, this is pages/api/og.tsx. The rough layout will look something like this as a boilerplate:
1import { ImageResponse } from "@vercel/og";
2import { NextRequest } from "next/server";
3
4export const config = {
5 runtime: "experimental-edge",
6};
7
8export default async function OpenGraphImage(req: NextRequest) {
9 const { searchParams } = new URL(req.url);
10 const slug = searchParams.get("slug") as string;
11
12 const data = await fetch(/* api call to fetch data from passed slug */)
13 return new ImageResponse(
14 (
15 <div
16 style={{
17 height: "100%",
18 width: "100%",
19 display: "flex",
20 flexDirection: "column",
21 alignItems: "center",
22 justifyContent: "center",
23 backgroundColor: "white",
24 }}
25 >
26 {/* populate with your dynamic data here */}
27 </div>
28 ),
29 {
30 width: 1200,
31 height: 630,
32 }
33 );
34}
TailwindCSS
Something that is still experimental but worked well for my use case is the TailwindCSS support for @vercel/og. While not all TailwindCSS classes seem to be supported from my experience, enough are to make it usable. See the below example of a generated image for another blog post:
Some other things to note:
- TailwindCSS support is provided via a tw prop rather then the className prop
- There are other limitations to @og/vercel as described here