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.
Technologies
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.
Sandbox
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.
Prerequisites
This post requires the following:
- Experience with JavaScript and React.js
- Installation of Node.js
- Next.js
- A Cloudinary account. Signup
Setup and Installations
First, we will create a Next.js boilerplate with the following command:
1npx create-next-app blog-thumbnail
Next, we’ll navigate into the project directory and install netlify-cli with the following command:
1cd blog-thumbnail2yarn add netlify-cli -g # installs Netlify CLI globally
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:
1yarn add cloudinary bootstrap file-saver
Generating thumbnails via Netlify function
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:
1[build]2 functions = "functions"
The above snippet tells Netlify where our functions will be located. In the terminal, run the following command to start the dev server:
1netlify 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:
1// functions/thumbnail.js2const Cloudinary = require("cloudinary").v2;3require("dotenv").config();45const handler = async (event) => {6 const { title, author } = JSON.parse(event.body);78 let today = new Date();9 let date =10 today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate();1112 try {13 Cloudinary.config({14 cloud_name: process.env.CLOUD_NAME,15 api_key: process.env.API_KEY,16 api_secret: process.env.API_SECRET,17 secure: true18 });1920 const imageTransform = Cloudinary.url("banners/banner.png", {21 transformation: [22 // base image resize23 {24 width: 720,25 height: 360,26 crop: "scale"27 },28 // Post Title overlay29 {30 overlay: {31 font_family: "Neucha",32 font_size: 40,33 font_weight: "bold",34 text: `${title}`35 },36 width: 400,37 height: 200,38 opacity: 100,39 gravity: "center",40 x: 35,41 color: "#00000098",42 crop: "fit"43 },44 // Post Author Overlay45 {46 overlay: {47 font_family: "Roboto",48 font_size: 14,49 font_weight: "bold",50 text: `${author}`,51 text_decoration: "underline"52 },53 width: 400,54 height: 200,55 opacity: 100,56 gravity: "south_west",57 x: 20,58 y: 18,59 color: "#fff",60 crop: "fit"61 },62 // Date of creation63 {64 overlay: {65 font_family: "Roboto",66 font_size: 14,67 font_weight: "bold",68 text: `created at:%250A${date}`,69 text_decoration: "underline"70 },71 width: 400,72 height: 200,73 opacity: 100,74 gravity: "south_east",75 x: 20,76 y: 10,77 color: "#fff",78 crop: "fit"79 }80 ]81 });8283 return {84 statusCode: 200,85 body: JSON.stringify({ data: imageTransform })86 };87 } catch (error) {88 console.log(error);89 }90};
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.
Fetch transformed Image
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:
1import axios from "axios";2import { useState } from "react";3import { saveAs } from "file-saver";4import "bootstrap/dist/css/bootstrap.css";56export default function IndexPage() {7 const [url, setUrl] = useState("");8 const [download, setDownload] = useState(false);910 const handleSubmit = async (e) => {11 e.preventDefault();1213 const userInput = {14 title: e.target.title.value,15 author: e.target.author.value16 };1718 const body = JSON.stringify(userInput);19 const config = {20 headers: {21 "Content-Type": "application/json"22 }23 };2425 try {26 if (body) {27 const response = await axios.post(28 "/.netlify/functions/thumbnail",29 body,30 config31 );32 const { data } = await response.data;33 setUrl(data);34 [e.target.name].value = "";35 }36 } catch (error) {37 console.error(error);38 }39 };4041 const handleDownload = async (e) => {42 saveAs(url, "thumbnail");43 setDownload(true);44 };45 return (46 <div className="">47 <nav className="navbar navbar-expand-sm navbar-white bg-white">48 <div className="container align-contents-center p-2">49 <form50 name="thumbnail-info"51 className="row g-3"52 onSubmit={handleSubmit}53 >5455 <div className="col-md-6">56 <label htmlFor="title" className="form-label">57 Post Title58 </label>59 <input60 name="title"61 id="title"62 className="form-control"63 required64 />65 </div>6667 <div className="col-md-6">68 <label htmlFor="author" className="form-label">69 Author's Name70 </label>71 <input72 name="author"73 id="author"74 className="form-control"75 required76 />77 </div>7879 <div className="col-12">80 <button type="submit" className="btn btn-primary btn-lg">81 Create thumbnail82 </button>83 </div>84 </form>85 </div>86 </nav>8788 <div className="container">89 {url ? (90 <div>91 <img92 src={url}93 className="img-thumbnail align-contents-center"94 alt="transformed banner"95 />96 <div>97 <button98 onClick={handleDownload}99 className={download ? "btn btn-success" : "btn btn-primary"}100 disabled={download ? true : false}101 >102 {download ? "Downloaded" : "Download"}103 </button>104 </div>105 </div>106 ) : (107 ""108 )}109 </div>110 </div>111 );112}
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
Conclusion
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.
Next steps
- 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