Automated social images on Netlify - with Next.js and Resoc
Tags: netlify, nextjs, tutorial
Published: 2021-09-27 06:53:23

The social images illustrate a web page on social networks and messaging services. For example, when I share [this previous article](https://blog.philippebernard.dev/replace-imagemagick-with-html-and-css) on Slack, I get:

![Social image example](https://cdn.hashnode.com/res/hashnode/image/upload/v1632402484731/-o5Il_40J.png)

The green and pink image is attached to the shared page via an `og:image` meta markup.

The social images are a great opportunity to get attention. More often than not, this opportunity is wasted, because creating those image is quite boring. Unless they are automatically generated for us.

In this tutorial, we are going to:
- Create a basic [Next.js app](https://nextjs.org/)
- Create an image template with HTML and CSS, with the [Resoc image template development kit](https://www.npmjs.com/package/itdk)
- Use this template to generate our social images, thanks to the [Resoc social image Netlify build plugin](https://www.npmjs.com/package/@resoc/netlify-plugin-social-image)
- Deploy our app to [Netlify](https://www.netlify.com/)

When sharing your app with your colleagues, they will see:

![Demo app on Slack](https://cdn.hashnode.com/res/hashnode/image/upload/v1632487923163/YGnfHhYx-.png)

If you can't wait, [try it now](https://demo-nextjs-on-netlify.netlify.app/) or [browse the code](https://github.com/Resocio/demo-nextjs-on-netlify) 😁

## Create the Next.js app

Let's create a new app and start it:

```bash
yarn create next-app
yarn dev
```

Before implementing the social images, we first fix a little something with meta management. In our fresh app, the `title` markup is hard coded in the homepage. This is okay in a simple example, but a real app will have this kind of recurrent markup moved to a single layout.

Replace the content of `pages/_app.js` with:

```jsx
import '../styles/globals.css'
import Head from 'next/head'

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <title>{pageProps.title}</title>
        <meta name="description" content={pageProps.description} />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp
```

Our app now expects two page props, `title` and `description`, which are injected in classic `head` markups.

We need to modify the homepage and pass these two variables. In `pages/index.js`, we define `getStaticProps` for this purpose. We also remove the demo content for clarity:

```jsx
import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      No need for content
    </div>
  )
}

export async function getStaticProps(context) {
  return {
    props: {
      title: 'Automated social images on Netlify',
      description: 'With Next.js and @resoc/netlify-plugin-social-image'
    }
  }
}
```

Visit the app again. From now on, title and description values come from the pages and `_app` prints the markups:

![Title and description meta markups](https://cdn.hashnode.com/res/hashnode/image/upload/v1632405548514/qbM_TYbkm.png)

Done with the setup!

## Create the image template

How should our social images look like? This is what we are going to work on, with the [Resoc image template development kit](https://www.npmjs.com/package/itdk). Quit `yarn dev` for a moment and run:

```bash
npx itdk init resoc-templates/default -m title-description
```

This command creates a new image template in `resoc-templates/default` and opens a viewer in a browser:

![Default template in the viewer](https://cdn.hashnode.com/res/hashnode/image/upload/v1632406245554/snjETYKXd.png)

The model we used via `-m title-description` takes two parameters: a title and a description. Later, we will make Next.js inject the correct metadata in the template, and Netlify create the images for us. For now, we modify the template so it matches our app.

Our app is gonna be hot, so let's replace the logo in the template. Overwrite `resoc-templates/default/logo.png` with the image below:

![New logo](https://cdn.hashnode.com/res/hashnode/image/upload/v1632407461997/qeJMGxow2.png)

In our browser, the viewer shows the update:

![Template with new logo](https://cdn.hashnode.com/res/hashnode/image/upload/v1632407954325/-vmyhwjjDY.png)

Now, change the background color. In `resoc-templates/default/styles.css.mustache`, replace the background definition of `.wrapper` with:

```css
background: rgb(247,58,12);
background: linear-gradient(90deg, rgba(247,58,12,1) 0%, rgba(247,99,12,1) 100%);
```

Our template is definitely hotter!

![Final template](https://cdn.hashnode.com/res/hashnode/image/upload/v1632407937535/kYpThrMeb.png)

That's all for the template. We could have been much more creative, for example by [imitating GitHub social images](https://blog.philippebernard.dev/automated-social-images-play-it-like-github). This is a Resoc template:

![GitHub social image with Resoc](https://cdn.hashnode.com/res/hashnode/image/upload/v1632465283833/1BZ2IMVNc.png)

## Generate social images

The idea behind image generation is pretty simple: take the title and description from a page, take the image template, mix, et voilà! The two technologies at work are [Mustache](http://mustache.github.io/), a template system used to inject parameter's values, and [Puppeteer](https://github.com/puppeteer/puppeteer), a headless Chrome system to convert HTML to images. However, we won't have to deal with these packages directly.

Each image needs a couple of seconds to be generated. With our demo app, that's not an issue. But in a real world example with hundreds of pages, this is clearly a concern. In particular, *we don't want this process to take place at build time*. Imagine your build time multiplied by 10... Cool social images don't worth such a wait. The [Resoc social image Netlify plugin](https://www.npmjs.com/package/@resoc/netlify-plugin-social-image) adresses the issue by using a Netlify Function to create our images on demand, not at build time. Problem solved.

Here is the process we are going to implement below:
- In our app, each page has a slug. No big deal: `index.js` is `homepage` (a particular case, since `index.js` is linked to `/`), `news.js` is `news`, `blog/huge-announcement.js` is `blog-huge-announcement`, etc.
- Pages register their image template parameters. In our app, each page saves its title and description for later use.
- The app layout declares the social image using the page's slug, eg. `/social-images/homepage.jpg`.
- A Netlify Function uses the image template parameters declared by the pages to actually create the social images, on demand.

Install the packages:

```bash
yarn add -D @resoc/netlify-plugin-social-image @resoc/core @resoc/img-data
```

We need to configure the plugin. At the root of the project, create `netlify.toml`:

```toml
[[plugins]]
package = "@resoc/netlify-plugin-social-image"
  [plugins.inputs]
  templates_dir = "resoc-templates"
  slug_to_image_data_mapping_file = "resoc-image-data.json"
  open_graph_base_path = "/social-images"
```

If you use Netlify you must be familiar with this syntax. But we need to explain those parameters.

`templates_dir` is the directory which contains our Resoc templates. Later, we will indicate that we want to use our template named `default`. This will be resolved as `resoc-templates/default`, which is where we created our sole template.

`slug_to_image_data_mapping_file` contains the data used to generate the social images.

`open_graph_base_path` is the base path we will use in our `og:image` markup.

We update `pages/index.js`:

```jsx
import styles from '../styles/Home.module.css'
import { storeImageData } from '@resoc/img-data'

export default function Home() {
  return (
    <div className={styles.container}>
      No need for content, really
    </div>
  )
}

const prepareStaticProps = async (title, description, imgSlug) => {
  await storeImageData(
    'resoc-image-data.json',
    imgSlug, {
      template: 'default',
      values: { title, description }
    }
  );

  return {
    props: {
      title,
      description,
      imgSlug
    }
  };
}

export async function getStaticProps(context) {
  return await prepareStaticProps(
    "Automated social images for Next.js on Netlify",
    "With getStaticProps and @resoc/netlify-plugin-social-image",
    "homepage"
  );
}
```

We added a helper called `prepareStaticProps`. Here, we call a function named `storeImageData`.  We pass it the title and description as template parameters, the page slug, and the template to use (we have only one and it is in the `default` sub directory). These data are saved to `resoc-image-data.json`, the file we assigned to  `slug_to_image_data_mapping_file ` in `netlify.toml`.

Because `prepareStaticProps` returns the page props, `getStaticProps` only needs to call it and return it as is.

This is good enough in our one page app. In a real app, `prepareStaticProps` should be reusable.

We must also update `pages/_app.js`:

```jsx
import '../styles/globals.css'
import Head from 'next/head'
import { FacebookOpenGraph } from '@resoc/core'

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <title>{pageProps.title}</title>
        <meta name="description" content={pageProps.description} />

        <meta property="og:title" content={pageProps.title} />
        <meta property="og:description" content={pageProps.description} />
        <meta property="og:image" content={`/social-images/${pageProps.imgSlug}.jpg`} />
        <meta property="og:image:width" content={FacebookOpenGraph.width} />
        <meta property="og:image:height" content={FacebookOpenGraph.height} />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp
```

We added the Open Graph markups.

`og:title`  and `og:description` are just a repetition of existing markups, Open Graph style.

To build the URL in `og:image`, we use the plugin parameter `open_graph_base_path` declared in `netlify.toml` and the page slug.

For completeness, we also declare the image width and height using  `FacebookOpenGraph.width` and `FacebookOpenGraph.height`.

We're done!

To make sure our app is in good shape, restart it:

```bash
yarn dev
```

Visit the homepage and inspect the `head` markup to make sure the Open Graph markups are correct.

![Open Graph markups](https://cdn.hashnode.com/res/hashnode/image/upload/v1632472561485/9GTkUkR27.png)

We also notice a new file, `resoc-image-data.json`. If was created by `getStaticProps`.

## Deploy to Netlify

`yarn create next-app` already initialized a Git repository for us.

Before we commit, let's add `resoc-image-data.json` to `.gitignore` to make sure it won't be committed. This file is generated at build time, which will be Netlify's responsibility.

Now, commit everything:

```bash
git commit -a -m "Social image!"
```

Log in to GitHub and create a new repository. To push your existing repository to GitHub, follow the instructions:

![GitHub instructions](https://cdn.hashnode.com/res/hashnode/image/upload/v1632475731558/f8QI2S5K3.png)

Now register to [Netlify](https://www.netlify.com/) or login. Add a new site:

![Add new site to Netlify](https://cdn.hashnode.com/res/hashnode/image/upload/v1632476297041/IZbeqwyc2.png)

Use the Wizard to select your repository, leave the default values as they are and click "Deploy site". After a couple of minutes, our app is built and deployed.

Get its URL:

![Netlify app URL](https://cdn.hashnode.com/res/hashnode/image/upload/v1632476668984/3NqZMaPPG.png)

If we visit this URL, there is not much to see. After all, this is not where our social image shine. Instead, submit it to the [Facebook debugger](https://developers.facebook.com/tools/debug):

![Facebook debugger](https://cdn.hashnode.com/res/hashnode/image/upload/v1632476853890/dAYxGRpuz.png)

Hurray! The social image of our homepage was generated!

## Conclusion

Automated social images have been around for a while. But creating them required a lot of custom code and efforts. Some solutions have been proposed, often at the expense of much longer builds.

Using the [Resoc image template dev kit](https://www.npmjs.com/package/itdk) and the [Resoc social image Netlify build plugin](https://www.npmjs.com/package/@resoc/netlify-plugin-social-image), we were able to setup automated social images quickly, with just the right amount of code. We even have spear time to perfect the template and make our images even more awesome!

I hope you enjoyed this article. If you followed this tutorial, I would be glad to get your feedback!