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
- 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.
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 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:
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 <Transformation10 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 <Transformation24 color='#FFFFFF'25 overlay={{26 fontFamily: 'Dancing Script',27 fontSize: 50,28 fontWeight: 'bold',29 text: `from ${name}`,30 }}31 />32 <Transformation33 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 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:
1//imports here2 import { Card } from '../components/Card'; //add34 export default function Home() {5 //states here67 const handleChange = (e) => {8 //handle change codes here9 };1011 //add12 const handleSubmit = (e) => {13 e.preventDefault();14 if (imageId) {15 setShowCard(true);16 } else {17 setFormData({ ...formData, error: true });18 }19 };2021 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>3233 {/* Conditionally render card Components */}34 {showCard && (35 <div className='mt-10'>36 <Card37 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 here23 import { useEffect, useRef, useState } from 'react'; //add45 export const Card = ({ message, name, publicId }) => {6 const ref = useRef(null);7 const [url, setURL] = useState('');8 const [copy, setCopy] = useState('Copy File');910 useEffect(() => {11 setURL(ref.current.element.current.src);12 return () => {};13 }, []);1415 const handleCopyToClip = () => {16 navigator.clipboard17 .writeText(url)18 .then(() => setCopy('Copied!'))19 .catch((err) => console.log('error copying to clipboard', err));20 };2122 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>2930 {/* 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 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:
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: