Automated social images on Netlify - with Next.js and Resoc
Create awesome, automated social images with HTML & CSS and just a few lines of code
The social images illustrate a web page on social networks and messaging services. For example, when I share this previous article on Slack, I get:
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
- Create an image template with HTML and CSS, with the Resoc image template development kit
- Use this template to generate our social images, thanks to the Resoc social image Netlify build plugin
- Deploy our app to Netlify
When sharing your app with your colleagues, they will see:
If you can't wait, try it now or browse the code 😁
Create the Next.js app
Let's create a new app and start it:
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:
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:
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:
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. Quit yarn dev
for a moment and run:
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:
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:
In our browser, the viewer shows the update:
Now, change the background color. In resoc-templates/default/styles.css.mustache
, replace the background definition of .wrapper
with:
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!
That's all for the template. We could have been much more creative, for example by imitating GitHub social images. This is a Resoc template:
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, a template system used to inject parameter's values, and 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 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
ishomepage
(a particular case, sinceindex.js
is linked to/
),news.js
isnews
,blog/huge-announcement.js
isblog-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:
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
:
[[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
:
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
:
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:
yarn dev
Visit the homepage and inspect the head
markup to make sure the Open Graph markups are correct.
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:
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:
Now register to Netlify or login. Add a new site:
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:
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:
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 and the Resoc social image Netlify build plugin, 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!