An Interest In:
Web News this Week
- April 19, 2024
- April 18, 2024
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
- April 13, 2024
Create OG images for your blog with Next.js
At the same time I released the new design for this blog using Next.js, Vercel announced a new library to generate OpenGraph (OG) images. You know, these images you can see when posting some links on Twitter.
Since my blog posts had a cover image associated, it sounded natural to me to use this image. But I often wonder if it was worth it to generate an image with additional content such as the post title or its date. It was pretty complicated to generate an image.
Fortunately, Vercels OG library now makes it much easier. Lets see how by implementing OG image generation for an imaginary blog.
If you are curious about Next.js and want to learn it, check out by book Serverless Web Applications with React and Next.js.
If you prefer video content, I created a short video with the content of this post:
@vercel/og
, satori
, and Edge functions
Generating OG images is made possible by the @vercel/og
library, itself relying on satori
(also made by Vercel). This package allows you to create an image from HTML and CSS code (using JSX). @vercel/og
adds a layer to make it easier to use, for instance by loading a default font so you dont have to.
On the client, such generation does not sound complicated, and weve known how to do it for a long time using a canvas
element. But on the server, you had to start a headless Chrome browser, which was complex and expensive. satori
doesnt work that way, and can generate images efficiently on the client, or in serverless functions.
Even better, it can run on Edge functions, and that is what @vercel/og
does. Edge functions are a special kind of serverless functions, that are free from all the classic Node.js runtime features. You can only use the basic JavaScript features, from the V8 runtime, so Edge functions comes with some limitations, but are extremely fast, and their execution cheaper.
Generate an image with @vercel/og
You can follow this tutorial on any Next.js project. Ill show how to start from an empty Next.js application, just generated with the command:
$ yarn create next-app hello-og --typescript
In this project, well use a serverless function to generate OG images, accessible at the endpoint /api/og
. Lets create it in a new /pages/api/og.tsx
file.
import { NextRequest, NextResponse } from 'next/server'export const config = { runtime: 'experimental-edge',}const handle = (req: NextRequest) => { return NextResponse.json({ hello: true })}export default handle
If you are used to serverless functions in Next.js, you might notice some differences:
- The exported
config
object. Here we tell Next.js that this function is an Edge function. - The
req
parameter does not have theNextApiRequest
type, butNextRequest
. This is also because its an Edge function. - We return a response (instead of using a
res
object passed as parameter), of typeNextResponse
.
Still, if you run the application and call the endpoint, you will get the expected JSON result, as with any classic function:
$ curl http://localhost:3000/api/og{"hello":true}
Now lets update this function to return an image using @vercel/og
. We first need to add the dependency to our project with yarn add @vercel/og
. Then we can generate a basic image containing the text Hello World!:
import { ImageResponse } from '@vercel/og'// ...const handle = (req: NextRequest) => { return new ImageResponse( <div>Hello World!</div>, // 1200x628 seems to be the right dimensions to use for OG images. { width: 1200, height: 628 }, )}
With these three lines of code returning an ImageResponse
, we generate an image from React code. That sounds easy, right? You probably think this is great, I can use all my React components! Well, its not that easy .
Even though we use JSX here, its important to understand that its just a shorthand offered by satori
(which seems to use React, but not React-DOM). The JSX code you write here has some constraints:
- Not all CSS properties are supported,
- Only
flex
layout is supported, and each element with multiple children must explicitely use (display: flex
), - You cant use
script
orlink
elements, etc.
Knowing these constraints, we can still do a lot. A feature I love: you can use TailwindCSS classes natively, using the tw
prop on elements (instead of className
). If you prefer, you can use the classic style
prop to specify CSS properties.
Lets assume we want to display a post title, an author name, and a date that well receive from query parameters. To get these parameters, we cant use req.query
: in Edge functions, we have to use req.url
, that we can parse using the URL
class:
const handle = (req: NextRequest) => { const { searchParams } = new URL(req.url) const title = searchParams.get('title') || 'No post title' const author = searchParams.get('author') || 'Anonymous' const date = new Date(searchParams.get('date') || '2022-11-05T12:00:00.000Z')
And then, we can use these parameters to add them to the image, with some TailwindCSS classes to make everything a bit more beautiful:
return new ImageResponse( ( <div tw="flex w-full h-full flex-col justify-end bg-slate-200 items-stretch"> <div tw="flex flex-col bg-white p-8"> <div tw="text-5xl mb-4">{title}</div> <div tw="text-2xl"> {author + ' ' + date.toLocaleDateString('en-US', { dateStyle: 'long' })} </div> </div> </div> ), { width: 1200, height: 628 } )}
Here is what this first version of our OG image looks like, with some parameters:
An image inside the image
This is nice, but it misses the most important thing we want on it: the cover image. Lets add a cover
parameter, and use it in an img
element:
const handle = (req: NextRequest) => { const { searchParams, host, protocol } = new URL(req.url) // ... const cover = `${protocol}//${host}${ searchParams.get('cover') || '/cover.jpg' }` return new ImageResponse( ( <div tw="flex w-full h-full flex-col justify-end bg-slate-200 items-stretch"> <img src={cover} alt="" tw="flex-1 w-full h-full" style={{ objectFit: 'cover', objectPosition: 'center' }} /> {/* ... */} </div> ), { width: 1200, height: 628 } )}
(To run the example, go get an image on Unsplash and put it in public/cover.jpg.)
Note how we use the host
and protocol
attributes from the requests URL, as we need to use absolute URLs for img
elements.
This second version is much more satisfying until we deploy it on Vercel. After deployment, if you call try to generate an image, you might get a blank image. In the Vercel logs, youll see this kind of error:
It isnt obvious, but the issue comes from the cover image we want to include, which seems too big for @vercel/og
at the time Im writing this post. We could choose to use a smaller image, but its pretty annoying
Good news, Next.js already provides an easy way to resize images. When using the next/image
component, Next.js uses an endpoint at /_next/image
. While we can not use the component, we can use the endpoint!
const cover = `${protocol}//${host}/_next/image?url=${encodeURIComponent( searchParams.get('cover') || '/cover.jpg')}&w=1200&q=75`
Add OG image to your pages
Now that we know how to generate our image, we can use it in the meta tags of the page headers. You can find the complete list of meta tags you need to define in Twitter documentation. I like using a custom component I built in many projects of mine: SeoHeaders
.
If you are adding OG images to a Next.js blog, I assume you already have a component in which you know the post title, author, date, and cover image. You can build the OG image URL:
// It must be an absolute URL, so I use an// environment variable for the base URL.const imageUrl = `${process.env.NEXT_PUBLIC_BASE_URL}/api/og?` + `title=${encodeURIComponent(title)}` + `&author=${encodeURIComponent(author)}` + `&cover=${encodeURIComponent(cover)}` + `&date=${date}`
Then, you can pass this URL to the SeoHeaders
component:
<SeoHeaders title={title} description="" author={author} twitterAuthor="" twitterSite="" url={process.env.NEXT_PUBLIC_BASE_URL} imageUrl={imageUrl}/>
Or, if you add the meta
tags by yourself:
<meta property="og:image" content={imageUrl} /><meta name="twitter:image" content={imageUrl} />
Note that so that the image is used in social media, you need to specify the other tags: og:type
, twitter:card
, etc.
This concludes this tutorial about generating and using OG images in a Next.js application! Our image is pretty minimalist, but here are a few ideas if you want to improve it:
- Play with opacity options (using TailwindCSS class
bg-opacity-50
for instance) instead of having a white background for the text. - Add the authors avatar (using a second image).
- Add the description to the image, or an excerpt of it.
- If your blog allows comments or likes, you can add some counter to the image.
Youll find the complete source code of the example Next.js application on this GitHub repo. Dont forget to check out the documentation of Vercels OG Image Generation.
This post was first posted on my blog: Create OG images for your blog with Next.js.
Cover photo by Noman Shahid.
Original Link: https://dev.to/scastiel/create-og-images-for-your-blog-with-nextjs-d5f
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To