If you own a personal blog or maintain your company’s blog, you will often find yourself creating OG images, thumbnails, and graphics for publishing every new blog post. This is very routine and very repetitive. Hence, it can be automated.
In this post, we’ll show you how to automatically generate your blog post images and thumbnails from Cloudinary when you supply the post title and author to a Netlify function.
Netlify Functions allow us to deploy server-side code that works as API endpoints, runs automatically in response to events, or processes more complex jobs in the background. Very recently, Netlify functions offering has grown with the addition of new features like functions scheduling (CRON) and Edge functions which we’ll talk about in a later post.
Cloudinary allows us to transform images and videos to load faster with no visual degradation. With Cloudinary, you can automatically generate image and video variants, and deliver high-quality responsive experiences to increase conversions.
If you’d like to get a headstart by looking at the finished demo, I’ve got it set up here on Codesandbox for you!
We completed this project in a CodeSandbox. Fork and run it to quickly get started.
This post requires the following:
- Experience with JavaScript and React.js
- Installation of Node.js
- Next.js
- A Cloudinary account. Signup
First, we will create a Next.js boilerplate with the following command:
npx create-next-app blog-thumbnail
Next, we’ll navigate into the project directory and install netlify-cli with the following command:
cd blog-thumbnail
yarn add netlify-cli -g # installs Netlify CLI globally
Code language: PHP (php)
Next, let’s also install the following yarn packages:
- Cloudinary – So we can interact with the Cloudinary APIs.
- Bootstrap – To handle our project styling.
- File-saver – Help save the generated thumbnail.
The following command installs the above packages:
yarn add cloudinary bootstrap file-saver
Next, create a netlify.toml
file in the root directory and add the snippet below to read form input and generate the post thumbnail. For a start, update the toml
file with this snippet:
[build]
functions = "functions"
Code language: JavaScript (javascript)
The above snippet tells Netlify where our functions will be located. In the terminal, run the following command to start the dev server:
netlify dev
Netlify will start a live dev server at http://localhost:8888
and we should see the project on that address in the browser.
Next, let’s create a functions
folder in the root directory with a thumbnail.js
file and add the following snippet:
// functions/thumbnail.js
const Cloudinary = require("cloudinary").v2;
require("dotenv").config();
const handler = async (event) => {
const { title, author } = JSON.parse(event.body);
let today = new Date();
let date =
today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate();
try {
Cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
secure: true
});
const imageTransform = Cloudinary.url("banners/banner.png", {
transformation: [
// base image resize
{
width: 720,
height: 360,
crop: "scale"
},
// Post Title overlay
{
overlay: {
font_family: "Neucha",
font_size: 40,
font_weight: "bold",
text: `${title}`
},
width: 400,
height: 200,
opacity: 100,
gravity: "center",
x: 35,
color: "#00000098",
crop: "fit"
},
// Post Author Overlay
{
overlay: {
font_family: "Roboto",
font_size: 14,
font_weight: "bold",
text: `${author}`,
text_decoration: "underline"
},
width: 400,
height: 200,
opacity: 100,
gravity: "south_west",
x: 20,
y: 18,
color: "#fff",
crop: "fit"
},
// Date of creation
{
overlay: {
font_family: "Roboto",
font_size: 14,
font_weight: "bold",
text: `created at:%250A${date}`,
text_decoration: "underline"
},
width: 400,
height: 200,
opacity: 100,
gravity: "south_east",
x: 20,
y: 10,
color: "#fff",
crop: "fit"
}
]
});
return {
statusCode: 200,
body: JSON.stringify({ data: imageTransform })
};
} catch (error) {
console.log(error);
}
};
Code language: JavaScript (javascript)
In the snippet above, we:
- Import the Cloudinary package, using the NodeJS require method.
- Import the
dotenv
package to handle our environment variables. - Create a handler function that takes in an event argument.
- Destructure the
post author
andpost title
from theevent.body
object, thenJSON.parse
it. - Configure our Cloudinary instance with credentials from the Cloudinary dashboard.
- Initiate the Cloudinary URL function, which takes in an image path and an array of transformations to be carried out on the base image. In our case, we want to overlay our
post title
andpost author
on the base image.
After successfully performing these transformations, Cloudinary returns a URL of the transformed image, which we then store in the imageTransform
variable.
Next, we need to fetch the returned URL of the transformed image and display it in the browser. To achieve this, lets update the pages/index.js
file. The final updates to the file should look like the snippet below:
import axios from "axios";
import { useState } from "react";
import { saveAs } from "file-saver";
import "bootstrap/dist/css/bootstrap.css";
export default function IndexPage() {
const [url, setUrl] = useState("");
const [download, setDownload] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
const userInput = {
title: e.target.title.value,
author: e.target.author.value
};
const body = JSON.stringify(userInput);
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
if (body) {
const response = await axios.post(
"/.netlify/functions/thumbnail",
body,
config
);
const { data } = await response.data;
setUrl(data);
[e.target.name].value = "";
}
} catch (error) {
console.error(error);
}
};
const handleDownload = async (e) => {
saveAs(url, "thumbnail");
setDownload(true);
};
return (
<div className="">
<nav className="navbar navbar-expand-sm navbar-white bg-white">
<div className="container align-contents-center p-2">
<form
name="thumbnail-info"
className="row g-3"
onSubmit={handleSubmit}
>
<div className="col-md-6">
<label htmlFor="title" className="form-label">
Post Title
</label>
<input
name="title"
id="title"
className="form-control"
required
/>
</div>
<div className="col-md-6">
<label htmlFor="author" className="form-label">
Author's Name
</label>
<input
name="author"
id="author"
className="form-control"
required
/>
</div>
<div className="col-12">
<button type="submit" className="btn btn-primary btn-lg">
Create thumbnail
</button>
</div>
</form>
</div>
</nav>
<div className="container">
{url ? (
<div>
<img
src={url}
className="img-thumbnail align-contents-center"
alt="transformed banner"
/>
<div>
<button
onClick={handleDownload}
className={download ? "btn btn-success" : "btn btn-primary"}
disabled={download ? true : false}
>
{download ? "Downloaded" : "Download"}
</button>
</div>
</div>
) : (
""
)}
</div>
</div>
);
}
Code language: JavaScript (javascript)
In the snippet above, we
- Import the necessary packages
- Create and export our page function.
- Return a JSX form containing inputs for the post title, author and a submit button
- Create a
handleSubmit
function to handle our form submission logic. - Pass the user data (post title & post author) in a
POST
request using Axios. - Use the
useState
hook to create a state variable calledurl
to store our transformed image URL. - Check if the transformed image
url
exists, if it does, we render the image. - Render a download button to call our
handleDownload
function which in turn downloads the generated thumbnail to our device using thesaveAs
package. - Finally, we used a condition to ensure that the download button is only visible when our function returns URL data.
When we have the above completed, you should see the following on your browser
Imagine being able to generate thumbnails for all your blog posts just by providing the title of your post and clicking a button. We’ve made that possible in this post as we’ve walked through the process of creating a Next.js application that does just that. Feel free to deploy this project on Netlify or any other deployment platforms of your choice and use it as you see fit.
- Replace the base image with your preferred image
- Generate all your thumbnails with ease
- Here are some links to learn more about Cloudinary and Netlify functions.
- The base image used for this post can be found here