Greeting cards are pieces of stiff paper or thin cardboard with text, illustrations, or photos, given on special occasions such as birthdays, anniversaries, holidays, e.t.c.
This post will discuss building a holiday card generator for the Harvest Month, Thanksgiving, and Diwali holidays using Cloudinary and Next.js. At the end of this tutorial, we will learn how to host images on Cloudinary and add transformations to the selected image.
Cloudinary is a platform on which you can quickly and easily upload, store, manage, transform, and deliver images and videos for websites and apps. The platform also offers a vast collection of SDKs for frontend frameworks and libraries.
Next.js is a React-based frontend development framework that enables functionalities like server-side rendering, static site generation, file-system routing (with no need to configure react-router-dom
manually), and API endpoints for backend features.
We completed this project in a CodeSandbox, and you can fork it to run the code.
The following steps in this post require JavaScript and React.js experience. Experience with Next.js isn’t a requirement, but it’s nice to have.
We also need a Cloudinary account to store the media files. Signup is completely free.
We need to create a Next.js starter project by navigating to the desired directory and running the command below in our terminal.
npx create-next-app -e with-tailwindcss holiday_card && cd holiday_card
Code language: JavaScript (javascript)
This command creates a Next.js project with TailwindCSS setup called holiday_card
, and navigates into the project directory.
TailwindCSS is a utility-first CSS framework packed with classes to help us style our web page.
We proceed to install the cloudinary-react dependency with:
npm install cloudinary-react
With the project dependencies installed, we need to download sample images to create our holiday cards. To get started, navigate to the URLs below and download the photos from Unsplash.
Harvest Month Photos
- https://unsplash.com/photos/fjyAh0NLowI
- https://unsplash.com/photos/yP19KADwhEI
- https://unsplash.com/photos/yuiJO6bvHi4
- https://unsplash.com/photos/6cC7WKiwcGs
- https://unsplash.com/photos/Nu4u9g7Sgdw
Thanksgiving Photos
Diwali Photos
Next, we need to log into our Cloudinary dashboard. Click on the Media Library tab, then drag and drop the downloaded images. Cloudinary also supports other upload formats.
After uploading all the images, we will see them displayed on the console with the publicId
. The IDs will come in handy when generating holiday cards.
With the images uploaded, we need to create a utils
folder in the project root directory. In this folder, we create harvest.json
, thanksgiving.json
, and diwali.json
files. These files contain data of the images for each type of card.
Here are the JSON data for each file.
harvest.json
[
{
"id": 1,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
},
{
"id": 2,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
},
{
"id": 3,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
},
{
"id": 4,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
},
{
"id": 5,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
}
]
Code language: JSON / JSON with Comments (json)
Thanksgiving.json
[
{
"id": 1,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
},
{
"id": 2,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
}
]
Code language: JSON / JSON with Comments (json)
diwali.json
[
{
"id": 1,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
},
{
"id": 2,
"publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
}
]
Code language: JSON / JSON with Comments (json)
Next, we need to modify the index.js
file in the pages
folder:
https://gist.github.com/Mr-Malomz/fbd7160578061f216fb3604a151cf57b
https://gist.github.com/Mr-Malomz/fbd7160578061f216fb3604a151cf57b
The snippet above does the following:
- Import required dependencies and image collections
- Create state variables to manage active tabs, selected Image ID, form data, and display the Holiday card.
- A
handleChange
function to control form inputs - Markups to conditionally display tabs
- Markups for form elements and conditionally render the list of Images using
cloudinary-react
. The list of images also has anonClick
function that sets the current imageid
to show the active image, the form object, and theshowCard
property.
With that done, we can start a development server using the command below:
npm run dev
With our application up and running, we need to create a Card
component to render the generated Holiday card. To do this, we need to create a components
folder in the project root directory, and in this folder, create a Card.js
file and add the code snippet below:
import { CloudinaryContext, Transformation, Image } from 'cloudinary-react';
export const Card = ({ message, name, publicId }) => {
return (
<div>
<CloudinaryContext cloudName='dtgbzmpca'>
<Image publicId={publicId} width={1000}>
<Transformation crop='fit' effect='blur:100' />
<Transformation effect='brightness_hsb:-50' />
<Transformation
color='#FFFFFF'
overlay={{
background: '',
fontFamily: 'Neucha',
fontSize: 100,
fontWeight: 'bold',
text: message,
textAlign: 'center',
}}
width='1300'
crop='fit'
/>
<Transformation flags='layer_apply' />
<Transformation
color='#FFFFFF'
overlay={{
fontFamily: 'Dancing Script',
fontSize: 50,
fontWeight: 'bold',
text: `from ${name}`,
}}
/>
<Transformation
flags='layer_apply'
gravity='center'
x='450'
y='350'
/>
</Image>
</CloudinaryContext>
</div>
);
};
Code language: JavaScript (javascript)
The snippet above does the following:
- Imports the required dependencies
- The
Card
component acceptsmessage
,name
, andpublicId
props. - Configure
CloudinaryContext
,Image
, andTransformation
s to render the image, message, and name. We also leverage Cloudinary’s support for multiple transformations to transform the image. We added the following transformations, cropping, blurring, brightening, adding overlays for text, text position**,** text properties, and flags to alter the positioning of the text.
Then we need to update index.js
by conditionally rendering the Card.js
component as shown below:
//imports here
import { Card } from '../components/Card'; //add
export default function Home() {
//states here
const handleChange = (e) => {
//handle change codes here
};
//add
const handleSubmit = (e) => {
e.preventDefault();
if (imageId) {
setShowCard(true);
} else {
setFormData({ ...formData, error: true });
}
};
return (
<div className='p-10'>
{/* Head JSX comes here */}
<main className=''>
<h1 className='text-3xl'>Holiday Card Generator</h1>
<header className='flex border-b-2 mt-7 mb-7'>
{/* Header contents JSX comes here */}
</header>
<form onSubmit={handleSubmit} className='lg:w-2/5'> {/* add handleSubmit */}
{/* Form contents JSX comes here */}
</form>
{/* Conditionally render card Components */}
{showCard && (
<div className='mt-10'>
<Card
message={formData.message}
name={formData.name}
publicId={formData.publicId}
/>
</div>
)}
</main>
</div>
);
}
Code language: JavaScript (javascript)
In the snippet above, we imported the Card
component, created an handleSubmit
function that checks if an image is selected, and then conditionally renders the Card
component with necessary props.
Complete index.js
https://gist.github.com/Mr-Malomz/736120f6f74832e3f1a3d1e21734529c
https://gist.github.com/Mr-Malomz/736120f6f74832e3f1a3d1e21734529c
We can test our application by starting the development server and creating different holiday cards.
Our card is incomplete if we can’t share it with friends and family. To do this, we need to modify the Card.js
file as shown below:
//imports here
import { useEffect, useRef, useState } from 'react'; //add
export const Card = ({ message, name, publicId }) => {
const ref = useRef(null);
const [url, setURL] = useState('');
const [copy, setCopy] = useState('Copy File');
useEffect(() => {
setURL(ref.current.element.current.src);
return () => {};
}, []);
const handleCopyToClip = () => {
navigator.clipboard
.writeText(url)
.then(() => setCopy('Copied!'))
.catch((err) => console.log('error copying to clipboard', err));
};
return (
<div>
<CloudinaryContext cloudName='dtgbzmpca'>
<Image publicId={publicId} width={1000} ref={ref}> {/* add ref */}
{/* Transformation JSX comes here */}
</Image>
</CloudinaryContext>
{/* Shareable link below */}
<div className='mt-10'>
<h5>Shareable link</h5>
<input disabled value={url} className='w-full lg:w-2/5 h-10 border-[#B7B3B3] border rounded-sm p-2 mr-4' />
<button className='bg-gray-600 py-2 px-6 rounded-[5px] text-white font-semibold' onClick={handleCopyToClip}>
{copy}
</button>
</div>
</div>
);
};
Code language: JavaScript (javascript)
The snippet above does the following:
- Imports required dependencies
- Create states and
ref
to access the Image DOM element - Update the
url
by accessing thesrc
property Cloudinary passed to theImage
component on render. -
handleCopyToClip
function that uses Clipboard API to copy the returned URL on our device clipboard - Add
ref
to theImage
component and markups for sharing links
Complete Card.js
https://gist.github.com/Mr-Malomz/87331d334f787cd75b6ce16d4b87e8f1
https://gist.github.com/Mr-Malomz/87331d334f787cd75b6ce16d4b87e8f1
The final output of our application should look like this:
This post discussed how to build a holiday card generator using Cloudinary’s image transformation and Next.js.
You may find these resources helpful: