Create a Holiday Card Generator

Emma Alder

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.

Sandbox

We completed this project in a CodeSandbox, and you can fork it to run the code.

Prerequisites

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.

Getting Started

We need to create a Next.js starter project by navigating to the desired directory and running the command below in our terminal.

1npx create-next-app -e with-tailwindcss holiday_card && cd holiday_card

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:

1npm install cloudinary-react

Holiday Card generator with Cloudinary

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

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.

Setting up the image collection

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

1[
2 {
3 "id": 1,
4 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
5 },
6 {
7 "id": 2,
8 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
9 },
10 {
11 "id": 3,
12 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
13 },
14 {
15 "id": 4,
16 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
17 },
18 {
19 "id": 5,
20 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
21 }
22 ]

Thanksgiving.json

1[
2 {
3 "id": 1,
4 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
5 },
6 {
7 "id": 2,
8 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
9 }
10 ]

diwali.json

1[
2 {
3 "id": 1,
4 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
5 },
6 {
7 "id": 2,
8 "publicId": "<REPLACE THIS WITH YOUR IMAGE PUBLICID>"
9 }
10 ]

Creating the holiday card

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 an onClick function that sets the current image id to show the active image, the form object, and the showCard property.

With that done, we can start a development server using the command below:

1npm 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:

1import { CloudinaryContext, Transformation, Image } from 'cloudinary-react';
2 export const Card = ({ message, name, publicId }) => {
3 return (
4 <div>
5 <CloudinaryContext cloudName='dtgbzmpca'>
6 <Image publicId={publicId} width={1000}>
7 <Transformation crop='fit' effect='blur:100' />
8 <Transformation effect='brightness_hsb:-50' />
9 <Transformation
10 color='#FFFFFF'
11 overlay={{
12 background: '',
13 fontFamily: 'Neucha',
14 fontSize: 100,
15 fontWeight: 'bold',
16 text: message,
17 textAlign: 'center',
18 }}
19 width='1300'
20 crop='fit'
21 />
22 <Transformation flags='layer_apply' />
23 <Transformation
24 color='#FFFFFF'
25 overlay={{
26 fontFamily: 'Dancing Script',
27 fontSize: 50,
28 fontWeight: 'bold',
29 text: `from ${name}`,
30 }}
31 />
32 <Transformation
33 flags='layer_apply'
34 gravity='center'
35 x='450'
36 y='350'
37 />
38 </Image>
39 </CloudinaryContext>
40 </div>
41 );
42 };

The snippet above does the following:

  • Imports the required dependencies
  • The Card component accepts message, name, and publicId props.
  • Configure CloudinaryContext, Image, and Transformations 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:

1//imports here
2 import { Card } from '../components/Card'; //add
3
4 export default function Home() {
5 //states here
6
7 const handleChange = (e) => {
8 //handle change codes here
9 };
10
11 //add
12 const handleSubmit = (e) => {
13 e.preventDefault();
14 if (imageId) {
15 setShowCard(true);
16 } else {
17 setFormData({ ...formData, error: true });
18 }
19 };
20
21 return (
22 <div className='p-10'>
23 {/* Head JSX comes here */}
24 <main className=''>
25 <h1 className='text-3xl'>Holiday Card Generator</h1>
26 <header className='flex border-b-2 mt-7 mb-7'>
27 {/* Header contents JSX comes here */}
28 </header>
29 <form onSubmit={handleSubmit} className='lg:w-2/5'> {/* add handleSubmit */}
30 {/* Form contents JSX comes here */}
31 </form>
32
33 {/* Conditionally render card Components */}
34 {showCard && (
35 <div className='mt-10'>
36 <Card
37 message={formData.message}
38 name={formData.name}
39 publicId={formData.publicId}
40 />
41 </div>
42 )}
43 </main>
44 </div>
45 );
46 }

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.

Adding a shareable link support

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:

1//imports here
2
3 import { useEffect, useRef, useState } from 'react'; //add
4
5 export const Card = ({ message, name, publicId }) => {
6 const ref = useRef(null);
7 const [url, setURL] = useState('');
8 const [copy, setCopy] = useState('Copy File');
9
10 useEffect(() => {
11 setURL(ref.current.element.current.src);
12 return () => {};
13 }, []);
14
15 const handleCopyToClip = () => {
16 navigator.clipboard
17 .writeText(url)
18 .then(() => setCopy('Copied!'))
19 .catch((err) => console.log('error copying to clipboard', err));
20 };
21
22 return (
23 <div>
24 <CloudinaryContext cloudName='dtgbzmpca'>
25 <Image publicId={publicId} width={1000} ref={ref}> {/* add ref */}
26 {/* Transformation JSX comes here */}
27 </Image>
28 </CloudinaryContext>
29
30 {/* Shareable link below */}
31 <div className='mt-10'>
32 <h5>Shareable link</h5>
33 <input disabled value={url} className='w-full lg:w-2/5 h-10 border-[#B7B3B3] border rounded-sm p-2 mr-4' />
34 <button className='bg-gray-600 py-2 px-6 rounded-[5px] text-white font-semibold' onClick={handleCopyToClip}>
35 {copy}
36 </button>
37 </div>
38 </div>
39 );
40 };

The snippet above does the following:

  • Imports required dependencies
  • Create states and ref to access the Image DOM element
  • Update the url by accessing the src property Cloudinary passed to the Image component on render.
  • handleCopyToClip function that uses Clipboard API to copy the returned URL on our device clipboard
  • Add ref to the Image 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:

Conclusion

This post discussed how to build a holiday card generator using Cloudinary’s image transformation and Next.js.

You may find these resources helpful:

Emma Alder

Technical Writer at Hackmamba.io

Technical writer at Hackmamba.io