Sanity Nextjs Social Image Generator

Github
Jordan McRae

Co-founder

6 min

Article Banner

This has been deprecated in favour of Vercel OG image generation.

Automatically generate social share images using Sanity webhooks, and your Next.js API!

Image generation flow example

Image generation flow example

sample of generated image

sample of generated image

Requirements

  • A Next.js application
  • A Sanity account and studio set up
  • A Redis database (we recommend Upstash - it's free!).

Note: Redis is necessary because we need a caching layer to prevent an infinite update loop in Sanity's webhooks when the library updates your social image. Also, we need to use a Redis service to solve this because Vercel's serverless functions are stateless, so we can not manage caching locally.

Setup

1) Create a webhook in Sanity

Whenever an article is updated in your Sanity studio, we want Sanity to notify our Next.js app, and send along the data we need to generate the social share image.

  • Log in to the Sanity Dashboard
  • Select your project, and click on the "API" tab
  • Click the "Create Webhook" button
  • Set up a webhook with the following options:

Name: Can be anything you want
URL: The URL your webhook will hit to trigger the image generation (if you're deployed to vercel, it might look something like: https://your-app.vercel.app/api/generate-preview-image).
Note: If you want to test this functionality in your local dev environment, you will need to use an Ngrok URL to point to your localhost. More information on this can be found here.
Dataset: "Production"
Trigger on: Create, Update
Filter: Depending on your Sanity schemas, you can define different types of documents you'd like to generate social share images for. For example, if you wanted to add a social share image to your articles or topic pages, you would use:

  • _type == 'article' || _type == 'topic' Projection: Please follow the same data structure as below. These are the pieces of data that the social image generator needs to properly generate your image:
1{ "text": title, _id, "imageUrl": image.asset->url }

Text: The text field you want to display on your social image. In the above example, it's our "title" field in our Sanity schema
imageUrl: The background image that you want to display on your social image. In the above example, it's our "image" field in our Sanity schema

webhook config

webhook config

  • Click the "Save" button at the bottom of the page.

2) Create a Sanity Editor Token

If your Next.js application does not currently have a Read / Write token in Sanity

  • In the API tab, scroll down to the bottom of the page and click on "Add API Token"
  • Add a name, like "Social Share Image Generation"
  • Select "Editor" under permissions
  • Click "Save"
  • Copy your token and save it in your Next.js environment variables. You will need this later!

3) Update your Sanity Schema

In your Sanity project, add a new field to the document schema that you will be generating social share images for.

  • Add the field

{ name : 'shareImage', title : 'Social Media Image', description: 'This is automatically generated by Next.js and can not be modified.', type : 'image', readOnly : true },

  • Publish your schema update (e.g. sanity deploy)

4) Install the package in your Next App

In your Next.js application, run the following command to install this package:

yarn add sanity-next-social-image-generator
or
npm install sanity-next-social-image-generator --save

5) Create a Next.js API Route

This is used to receive Sanity's webhook requests.

  • Create a new file in your API folder called: pages/api/generate-preview-image.js
  • Paste the following code into this file:
1import { createImageClient } from 'sanity-next-social-image-generator';
2
3// Create the client
4const client = createImageClient({
5  dataset: 'production',
6  projectId: process.env.SANITY_PROJECT_ID,
7  redisUrl: process.env.REDIS_URL,
8  token: process.env.SANITY_READ_WRITE_TOKEN,
9});
10
11const logo = require('./my-logo.jpg');
12
13export default async function generatePreviewImage(req, res) {
14  // Generate the image when Sanity's webhook hits your API
15  const { imageUrl, text, _id, _type } = req.body;
16
17  try {
18    await client.generateImage({
19      id: _id,
20      backgroundImageUrl: imageUrl,
21      text,
22      blur: 10,
23      darken: 50,
24      logo,
25      logoPosition: 'bottomRight',
26    });
27    res.status(200).send('Ok');
28  } catch(e) {
29    res.status(500).send(e);
30  }
31};

6) Update your Next.js page(s)' meta tags

In your Next.js page where you are using your article data, make sure you add your new image to your meta tags so that is shows up when your share your article!

Example page:

1import Head from 'next/head';
2import createImageUrlBuilder from '@sanity/image-url';
3
4const urlFor = (source) => createImageUrlBuilder(config).image(source);
5
6export default function MyArticlePage({ data }) {
7  return (
8    <div>
9      <Head>
10        {/* Add your meta tags */}
11        <meta property="og:image" content={urlFor(data?.page?.shareImage?.asset).url()} />
12        <meta name="twitter:image" content={urlFor(data?.page?.shareImage?.asset).url()} />
13      </Head>
14      {/* Page Content */}
15    </div>
16  );
17}
18
19export async function getStaticProps() {
20  // Fetch your article data from Sanity
21  const page = fetch('...');
22
23  return {
24    props: {
25      data: {
26        page
27      },
28    },
29  }
30}

Your app is now set up to receive Sanity webhooks and automatically generate your social share images!

API

Client

The client is used to authenticate with Sanity and upload your social share images. It takes the following arguments:

1const client = createImageClient({
2  dataset: 'production',
3  projectId: 'zza4oo0z',
4  redisUrl: 'rediss://username:password@url.upstash.io:37535',
5  token: '1z2n78hds83o8xnfgm0921ufh25ltri32z90-lsz2jn2b4k7db82',
6});

dataset: The Sanity dataset you want to update (e.g. production)
projectId: Your Sanity project ID
redisUrl: The full URL of your Redis instance. We recommend using Upstash since it's free!
token: Your Sanity Editor token (used for updating your article data with the social share image)

Image Options

There are many options available to you that you can use to control how your social share image will look.

All available options

1const image = await client.generateImage({
2  id: '4e40b6bf-9ad3-4c8d-a6b3-a240074c3ed0', 
3  backgroundImageUrl: 'https://i.picsum.photos/id/640/1200/600.jpg?hmac=jjcV1z6Vi_dLyTdWLTS8YNiQ6MNzPoggXvEF0Lji7dE',
4  text: 'Generating Social Share Images Automatically with Sanity and Next',
5  width: 1200,
6  height: 600,
7  backgroundFit: 'cover',
8  fontSize: 50,
9  fontName: 'Arial Black',
10  fontColor: 'white',
11  blur: 5,
12  logo: 'https://www.stackfive.io/images/stackfive-logo-large.png',
13  logoPosition: 'bottomRight',
14  filterColor: '#07ae9d',
15  darken: 25,
16  lighten: 1.25,
17});

id: Required The ID of the document you are adding a social share image to
backgroundImageUrl: Required The URL for the main image on your social share image. This can be any image, but typically you will use an image you define in Sanity (see Creating a Webhook in Sanity for more details)
text: Optional The text that will be displayed on your card
width: Optional The width of your social share image
height: Optional The height of your social share image
backgroundFit: Optional How the image should be resized to fit the dimensions. Can be one of cover, contain, fill, inside or outside.
fontColor: Optional The color of your text
fontSize: The size of your font. Note: You might need to modify this if you change the default width and height of the social share image
fontName: Optional The name of the font you want to use (font family)
blur: Optional Add blur to your background image
darken: Optional Darken your background image (# from 1-100)
lighten: Optional Brighten up your background image (each number is a multiplier)
logo: Optional The URL or Buffer of your logo
logoPosition: Optional Where you would like to place your logo on the image. Can be one of topLeft, topRight, bottomRight, or bottomLeft.
logoWidth: Optional Adjust the width of your logo
logoHeight: Optional Adjust the height of your logo
logoFit: Optional How the image should be resized to fit the dimensions. Can be one of cover, contain, fill, inside or outside.
filterColor: Optional Add a filter color overlay over the background image. Must be a hex code, or a valid css color type.

Creating an NGROK URL for Local Testing

If you want to test this functionality locally, your Sanity webhook will have to point to an NGROK URL. Ngrok tunnels your local environment, and creates a public URL.

Setup:

  1. Follow the NGROK setup guide here
  2. Run the following command in your terminal to expose your Next.js application: ngrok http 3000
  3. Copy the https URL that NGROK provides, and update your Sanity webhook URL to use this instead

If you followed the above steps, the Sanity webhook should now start hitting your localhost API. You can also configure a production and a dev webhook to use two different URLs if you wish.

Known Issues

M1 Macbooks

Node 16.x may cause issues on M1 Macbooks due to the Sharp library being used to process images.
If you run into an error in your Next.js application when using this library that says Module parse failed: Unexpected character '?', try updating your local Node version to 17.x.

Contributing

Pull requests are welcomed! Husky is set up to lint and format your code. Please also update the README.md file with any modifications you make.
There is a sample tool set up in sample/index.js to generate and save image files locally. You can run yarn sample to run this code while you are testing. The output file will be saved in /sample/output/sample.jpg.

Issues and Feature Requests

Please open an issue if you face any bugs, or would like to request new features.