Conference organizers often need to issue conference attendance tags to users who sign up to attend their conferences. This is often a daunting task requiring them to manually create and customize these tags for the hundreds and possibly thousands of attendees.
What if we can automate the process and generate a visually appealing conference tag upon registration for every attendee using Cloudinary and Next.js? That is what we'll discuss in this post.
First, a brief about both technologies:
Cloudinary is a modern asset management tool that helps you create, manage and deliver high-quality digital experiences to users. If you’d like to follow along with this post, you’ll need to create a free Cloudinary account.
Next.js is a React-based frontend framework that supports server-side rendering and static site generation. We’ll be building the tag generation project with it.
If you’re eager to get a quick look at the finished product, it is available here on Codesanbox
and also hosted on Github.
Prerequisites
Before we proceed, let’s make sure you've correctly set up and ready to build along. You will need the following:
- Some experience with JavaScript and Next.js.
- Node.js installed for local development.
- Some experience with Cloudinary.
- Some experience styling with Bootstrap
Setup and Installations
Run the following command in the terminal to create a new Next.js application named conference-tags
:
1npx create-next-app conference-tags
This will create a conference-tags
application for you. Next, let’s change directory into the conference-tags
project we just created:
1cd conference-tags # to navigate into the project directory
Next, we'll install the following npm packages:
- @cloudinary/react - so we can easily use Cloudinary in a React environment, and
- @cloudinary/url-gen to add different Cloudinary transformations and also generate a URL.
Run the command below to install the packages above:
1npm install @cloudinary/react @cloudinary/url-gen
Setting up Cloudinary console
Since we want to have a base image hosted on Cloudinary that we can fetch and add transformations to, we’ll first need to upload it to Cloudinary. You can access the base image I uploaded here and reuse it for your own demo if you’re building along.
For every user that registers for our conference, we want to:
- Overlay their details on the base image we currently have on Cloudinary
- Upload the new image (now containing the user’s data) to Cloudinary
- Finally display the image with a link for the user to download their tags.
To do that, let’s first set up a Cloudinary upload preset. Log into your Cloudinary account and click on Settings > Upload.
Now, scroll down to upload presets and click on Add upload presets. Then:
- Provide an upload preset name.
- Set signing mode to unsigned, and
- Set the desired folder to hold the image uploads.
Now that we have the initial Cloudinary settings out of the way, we can start building our tag generator application. First, run the development server with npm run dev
to visualize the changes in the browser as we build the project.
Next, open the project folder in your preferred code editor and create a components folder in the root of the project. Inside it, create a new Tag.js
file and update it with this snippet:
1// components/Tag.js2import { Cloudinary, Transformation } from "@cloudinary/url-gen";3import { AdvancedImage } from "@cloudinary/react";4import { source } from "@cloudinary/url-gen/actions/overlay";5import { image, text } from "@cloudinary/url-gen/qualifiers/source";6import { scale } from "@cloudinary/url-gen/actions/resize";7import { Position } from "@cloudinary/url-gen/qualifiers";8import { compass } from "@cloudinary/url-gen/qualifiers/gravity";9import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";10import { byRadius } from "@cloudinary/url-gen/actions/roundCorners";11import { brightness } from "@cloudinary/url-gen/actions/adjust";1213export default function Tag({ formData }) {14 // component code here15}
The Tag
component above will represent every tag we generate for every user when they sign up to attend this conference. What we’ve done so far is import all the Cloudinary packages we’ll need to achieve the desired transformation.
Next, let’s create a new Cloudinary instance with our cloud_name
to establish a connection between this project and our Cloudinary account:
1// imports here2export default function Tag({ formData }) {3 const cld = new Cloudinary({4 cloud: {5 cloudName: process.env.NEXT_PUBLIC_CLOUD_NAME,6 },7 });8}
With that, we can now define a baseImage
variable which can directly access our Cloudinary account to get the already existing base image:
1// imports here2export default function Tag({ formData }) {3 // init cloudinary4 let baseImage = cld.image(`base-tag`);5}
With that, we can now apply transformations on the base image to overlay the user’s details on it. Before we proceed, here’s something to note:
The Tag
component will be rendered in the index.js
file where we’ll accept the user data via a registration form and then pass that data to the Tag
component as props. The props will give us access to the name
, role
, and avatar
of the users which we can then overlay the base image like so:
1// src/components/Tag2export default function Tag({ formData }) {3 const { name, role, avatar } = formData;4 baseImage.overlay(5 source(6 image(`${avatar}`).transformation(7 new Transformation()8 .resize(scale().width(400).height(400))9 .roundCorners(byRadius(230))10 .adjust(brightness(5))11 )12 ).position(new Position().gravity(compass("center")).offsetY(-50))13 );14 baseImage.overlay(15 source(16 text(`${name}`, new TextStyle("Nunito", 65)).textColor("white")17 ).position(new Position().gravity(compass("center")).offsetY(210))18 );19 baseImage.overlay(20 source(21 text(`${role}`, new TextStyle("Nunito", 70)).textColor("purple")22 ).position(new Position().gravity(compass("center")).offsetY(350))23 );24 return();25}
Finally, we render the transformed base image for the user to see via the Cloudinary <AdvancedImage />
component:
1// src/components/Tag2export default function Tag({ formData }) {3// other component code4return (5 <div>6 <AdvancedImage cldImg={baseImage} />7 </div>8 );9}
With that, we are done with the Tag
component. You can find the complete Tag.js
component code embeded in this Github gist.
Moving on, let’s update the index.js
file to create the registration form and collect the user data when the form is submitted.
1// src/pages/index2import Head from "next/head";3import Image from "next/image";4import styles from "../styles/Home.module.css";5import { useState } from "react";6import { Form, Button } from "react-bootstrap";78const initialState = { name: "", avatar: "", role: "" };9export default function Home() {10 const [formData, setFormData] = useState(initialState);11 const [disableInput, setDisableInput] = useState(false);12 const [imageLoaded, setImageLoaded] = useState(true);13 // more component code here14}
What we’ve done in the Home
page above is import Next.js and Bootstrap components that we’ll be using shortly to build out the form. We also initialized some relevant variables using the useState
hook for the same purpose. In the form, we want to have 3 fields for name
, role
, and avatar
like so:
1//src/pages/index2//import statements here34const initialState = { name: "", avatar: "", role: "" };5export default function Home() {6return (7 <div>8 <main>9 <h1>10 Register to attend iJam Conf'22!11 </h1>12 <div>13 <div>14 <Form >15 <Form.Group>16 <Form.Label>Name</Form.Label>17 <Form.Control18 type="text"19 name="name"20 required21 disabled={disableInput}22 onChange={handleChange}23 value={formData.name ?? ""}24 />25 </Form.Group>26 <Form.Group controlId="formFile">27 <Form.Label>Avatar</Form.Label>28 <Form.Control29 type="file"30 accept="image/*"31 onChange={(e) => handleImage(e)}32 disabled={disableInput}33 />34 </Form.Group>35 <h4>Choose Role</h4>36 <div>37 <Form.Check38 type="radio"39 label={`Speaker`}40 value={`SPEAKER` ?? ""}41 name="role"42 onChange={handleChange}43 />44 <Form.Check45 type="radio"46 label={`Attendee`}47 value={`ATTENDEE` ?? ""}48 name="role"49 onChange={handleChange}50 />51 </div>52 <div>53 <Button54 onClick={(e) => {55 e.preventDefault();56 setShowTag(true);57 setDisableInput(true);58 setImageLoaded(true);59 }}60 type="submit"61 variant="primary"62 disabled={imageLoaded}63 >64 Generate Tag65 </Button>66 </div>67 </Form>68 </div>69 </div>70 </main>71 </div>72 );73}
Here, we have a simple form with three fields to collect the users name, avatar and their roles respectively. At the moment, the code will error out since we are calling functions in it that we haven’t defined yet. The handleChange()
and handleImage()
functions — let’s define them below:
1//src/pages/index2//import statements here34const initialState = { name: "", avatar: "", role: "" };5export default function Home() {6 const [formData, setFormData] = useState(initialState);7 const [disableInput, setDisableInput] = useState(false);8 const [imageLoaded, setImageLoaded] = useState(true);910 const handleChange = async (e) => {11 e.preventDefault();12 setFormData({ ...formData, [e.target.name]: e.target.value });13 };1415 const handleImage = async (e) => {16 const reader = new FileReader();17 reader.onloadend = async () => {18 setImageLoaded(true);19 };20 const files = e.target.files[0];21 if (!files) return;22 const data = new FormData();23 data.append("file", files);24 data.append("upload_preset", "conference_tags");25 const res = await fetch(26 "https://api.cloudinary.com/v1_1/kennyy/image/upload",27 {28 method: "POST",29 body: data,30 }31 );32 const file = await res.json();33 setFormData({ ...formData, avatar: file.public_id });34 setImageLoaded(false);35 };36 // return() function here37}
The handleChange()
function updates the value of our formData object in state with the value of the input fields as the user fills the form.
When the user clicks on the field to choose an image, we call the handleImage()
function to access our filesystem, select an image, upload the selected image to Cloudinary and update our formData
object with the publicID
of the uploaded image. At this point, if we check back on the browser, we should see the registration form:
With this, we have now accounted for all the fields in our form. We have access to the name
of the user, the role
they've selected and their avatar
. As a result, we can render our Tag
component and pass the user data to it. The component will accept the details as a prop and then overlay it on our base image and finally generate the conference tag for the user:
1{showTag && (2 <Tags3 formData={formData}4 />5)}
And with this, we are now successfully passing the user data to our Tag
component. The complete index.js
file is available in this gist for your consumption.
And now, the moment of truth. Let's take this project for a spin!
Summary
In this post, we've leveraged Cloudinary's image transformation capabilities to automatically generate conference tags on the fly for all users registering for our iJam conference. If you'd like to extend this project, you're welcome to fork this repo and make changes as you see fit.