Open Graph Image(s)
In the previous part, we saw how to use the Open Graph protocol in our metadata object, but an important part is still missing, which is why in this part, we are going to focus on creating open graph images
Next.js comes with a great feature called next/og which under the hood uses @vercel/og
To get a better idea of what it can do, I recommend visiting the "OG image" playground, which has some good examples of what can be achieved
Next OG images can do more than just use a static image as opengraph image. You can import custom fonts and images and even use CSS and HTML to build your opengraph scene, then @vercel/og will convert your code into an image using Satori (and Satori is based on resvg-js)
Static Open Graph Image (and static alt)
First, we will create a simple static opengraph image
We need 2 static assets to compose our og image, a font and a background image, as font we will use the same font we used in our layout.tsx
for the next/font examples, which is Kablammo from google fonts
To get the font visit https://fonts.google.com/specimen/Kablammo, and then click on the Get font button. On the next page, click on the Download all button (if the button is not there, first click on the shopping bag icon in the top right of the screen). Next, go into the public folder of your project and create a new /fonts
folder. Now go into your download folder and unzip the font package (which you just downloaded from google fonts). Open the /static
folder, and then copy the Kablammo-Regular.ttf
file into the /public/fonts
folder.
Our font is pretty big, it is >500kb, which is quite heavy but still ok. Usually, fonts are smaller. Be careful, when choosing a font as some might be bigger than that. If you choose a font over 2MB, you will get the following error:
Error: Failed to set Next.js data cache, items over 2MB can not be cached
As image, we will NOT reuse the one we downloaded earlier in the "How to NOT add images to an MDX document" part, because it is very heavy (>2.25MB) (and this time we won't have next/image optimize the image), a font that is >2.25MB would be too heavy (see cache error above).
Instead, we can go back to the "Stranger Things 2 Sign in City at Night" download page, click again on Free download, and this time choose the Medium 1280x800 version. The recommended size for opengraph images is 1200x630, so our 1280x800 background image will only be a little bit too big, which is good enough. At a file size of 178kb, it is also much smaller than the other version.
Or, if you know how to use an image editor like Gimp, you could use the original image and resize it to be exactly 1280x800 pixels.
Finally, move the image to the /public/images
folder and rename it to og_background.jpg
If you only need one background image, you could resize and optimize it manually by using an online tool like ezgif.com
If you need more than one image, then you might consider using sharp; which is the package that Next.js when it optimizes and resizes images locally; you could write a little script that automates the optimization of all the images you plan on using (as backgrounds or inside of your opengraph images)
Now that we have our assets go into the /app/blog
folder and create a new opengraph-image.tsx
file:
Line 1: we import the Next.js opengraph image tool
Line 3: we set a static title for all our opengraph images, this variable will be used for the alt value of the image and inside of the image itself
Lines 5 to 14: we export some variables that will tell Next.js metadata what values to use for the opengraph tags (the og:image tags which are related to the image)
Lines 21 to 28: we use fetch to get both the font file and the background image
Lines 33 to 72: we use basic HTML and CSS to create the content of our opengraph image; we created a div that covers the whole width and height and used the background image to fill it; then we put a smaller div inside, using position: absolute to position it over the background; that div will also display the title; we finally made the background of the title div a bit darker using a black color with the opacity set 0.5 to make it semi transparent
Make sure the dev server is running and then visit the http://localhost:3000/blog
blog page. If you then look at the tags inside of the <head>
element, you will notice that Next.js has added 5 new opengraph tags for our image:
The "metadataBase" problem, 404 response
There is one problem related to the metadataBase, where the opengraph image will just return a 404
You might see an error like this in the terminal:
⚠ metadataBase property in metadata export is not set for resolving social open graph or twitter images, using "http://localhost:3000". See https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadatabase
If that is the case, the workaround I found to work well is to set the metadataBase in the root layout to a default value like this:
Lines 9 to 11: we set the default metadataBase to use localhost in development and VERCEL_URL (otherwise if you don't use Vercel to deploy, either use an environment variable that works for your deployment tool or just set a static value that contains your domain)
Dynamic Open Graph Image
Static images are a great start, but what about more advanced scenarios where you might need a dynamic opengraph image generator
You could, of course, duplicate the opengraph script we just did and add one in each of your pages folders, but it is also possible to create a dynamic opengraph image using a tsx file
If like me, you are on the Vercel free plan (Hobby plan), be careful NOT to use images that are very heavy or use a lot of images (or other assets like fonts) in your og image script because Vercel functions have a limit of 1MB; so if your PNG background image is 1.5 MB you will get this error during the build process:
"Error: The Edge Function "web_development/opengraph-image" size is 1.68 MB, and your plan size limit is 1 MB. Learn More: https://vercel.link/edge-function-size"
In case you wonder Hobby: 1 MB, Pro: 2 MB, Enterprise: 4 MB
Dynamic OG Image for dynamic route segment
If you use dynamic route and both the page(.tsx/.jsx) file and the opengraph-image(.tsx) file are in the same folder, then you can use a dynamic route segment for the post title (or a post id if you prefer)
Let's create a new folder /posts
in our /app/blog
folder, and then inside of it another /[title]
folder that will act as a dynamic segment for our route
Finally, inside our new /app/blog/posts/[title]
folder, create an opengraph-image.tsx
file:
The code is very similar to the code we used for the "static" version. One difference is that we removed the static title
variable, and we also don't export the sizes
variable anymore (those static exports get replaced by the usage of the generateImageMetadata function lines 17 to 27)
Lines 11 to 15: we added an interface to strictly type the Image function props
Lines 17 to 27: we use the generateImageMetadata function to create dynamic opengraph image metadata. The alt value will be dynamic because we use the title
param (that contains the title value from our dynamic route segment)
Line 31: we also get the title
param inside of the Image function, as this is the value we will use now inside of our span line 83 as a replacement for the previously static title
The rest of the code, including the styling, has not changed (compared to the previous static example)
The last step is to create the posts page (to simplify the code of this example, we will only add the dynamic metadata part and not fetch any blog post content from the filesystem or database, but you could do just that using the Next.js generateStaticParams function):
Line 2: we import the notFound function from the next/navigation package; what the notFound function does is well explained in the typescript tooltip:
In a Server Component, this will insert a meta tag and set the status code to 404. In a Route Handler or Server Action, it will serve a 404 to the caller.
Line 4: as we will deal with params, which are content the user can modify, we must make sure those values get sanitized before we use them as a file path or in a database query; another solution would be to use a whitelist to exclude any dynamic value that does not match the values we have whitelisted, which is what we will do in this example
Lines 6 to 20: we use the Next.js generateMetadata function to get the title from the params; we then check if it is in our whitelist and if it is valid we create the metadata
Lines 28 to 41: we do the same whitelist check again; if valid we use the title variable in our JSX; if invalid we use the notFound function from next/navigation to create a 404 response
Make sure the dev server is running, and then you can visit the http://localhost:3000/blog/posts/foo
dynamic page to have a look at the dynamic opengraph image metatags inside of <head>
element
Now that you know how to create dynamic versions, you could even go further, if for example you use similar code to the what we used to make our sitemap dynamic. You could read the content of MDX files, extract the frontmatter and then use the frontmatter values for your dynamic opengraph images
Congratulations 🎉 you now know how to create static and dynamic opengrapgh images
If you liked this post, please consider making a donation ❤️ as it will help me create more content and keep it free for everyone