Build a concert tickets generator with NextJS using Xata and Cloudinary

Odewole Babatunde Samson - Nov 30 '22 - - Dev Community

As events evolve in the digital landscape, we might have seen some fun, engaging, and personalized event tickets shared on social media. This innovation is fascinating and showcases just how many excellent low-cost services and capabilities exist in web development.

This post will discuss building concert tickets using Cloudinary, Xata, and Next.js. At the end of this tutorial, we will learn how to store images on Cloudinary and explore the rich capabilities of Xata, a serverless database.

Check here for the source code on GitHub and the demo here.

Prerequisites

The knowledge of the following is required:

  • Knowledge of JavaScript and React.js
  • Node.js and its package manager, npm. We run the command node -v && npm -v to verify we have them installed or install them from here
  • Signup for a free Cloudinary account
  • A Xata account
  • Understanding Next.js would help us quickly follow this tutorial

Creating a Next.js application

To create the Next.js app, we go to our terminal or command line. Using the cd command, we navigate to the directory we want our to create our app.

cd <directory-name>
Enter fullscreen mode Exit fullscreen mode

Once inside the directory, we create a new project using the command:

npx create-next-app

# or

yarn create next-app
Enter fullscreen mode Exit fullscreen mode

Once that's finished, we navigate into that directory and start a hot-reloading development server for the project on http://localhost:3000 with:

npm run dev
#or
yarn dev
Enter fullscreen mode Exit fullscreen mode

Installing Cloudinary

Cloudinary provides a robust solution to store, transform, optimize and deliver images and videos in software applications.

Install the cloudinary-react package that exposes various media delivery and transformation functions using the command line.

npm i cloudinary-react lodash
Enter fullscreen mode Exit fullscreen mode

Lodash is a dependency of the Cloudinary package.

Installing TailwindCSS

TailwindCSS is a utility-first CSS framework packed with classes to help us style web pages.

We install tailwindcss and its peer dependencies via npm, which generates tailwind.config.js and postcss.config.js.

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

We need to add the paths to all of our template files in the tailwind.config.js file.

//tailwind.config.js
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
Enter fullscreen mode Exit fullscreen mode

We should add the @tailwind directives for Tailwind’s layers to our ./styles/globals.css file.

//globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Setting up the Xata database

Let’s create a new database on our Xata dashboard called concert-ticket

xata-database

Click on the created database and add a table titled concert. Next, add concert_name and artist column of type String to the table.

Our database should look like this:

xata-schema

Setting up the Xata instance

To set up Xata, we'll need to install the CLI globally:

npm install @xata.io/cli -g
Enter fullscreen mode Exit fullscreen mode

Then, authorize Xata to log us in:

xata auth login
Enter fullscreen mode Exit fullscreen mode

Next, we select Create a new API key in the browser from the prompts in our terminal. This opens our browser, where we can type in any name we choose. Once successful, we will get a display page indicating that we are all set.

Now, let’s cd into the Nextjs project created earlier and run xata init to initialize Xata in our project;

cd <directory-name>
xata init
Enter fullscreen mode Exit fullscreen mode

This is where we select the name of the database created earlier, then select Generate Javascript code with ES modules code and choose our output source as src/xata.js which is where the Xata codegen will be generated.

Image Upload to Cloudinary

In our dashboard, we will have access to upload and save the downloaded images in Cloudinary by clicking the Media Library tab and Upload button, as displayed below.

cloudinary-media

Next, we need to copy the publicId for the saved images from our dashboard, which will be necessary to generate the article banner.

cloudinary dasshboard

Setting up the image collection

We’ll create a utils folder in the project root directory, where we need to create a ticket.json file to hold to data of the uploaded images.

Here is the JSON data for the ticket.json file.

[
    {
        "id": 1,
        "publicId": "photo-1649894221695-45abb718a190_sgjhwd.jpg"
    },
    {
        "id": 2,
        "publicId": "photo-1649894222226-056a1a79d9fb_xlv73h.jpg"
    },
    {
        "id": 3,
        "publicId": "photo-1648800475313-2bb7fbec8701_ae60yw.jpg"
    },
    {
        "id": 4,
        "publicId": "photo-1647067867267-e01d98462f3c_ugtnwe.jpg"
    },
    {
        "id": 5,
        "publicId": "photo-1644241687200-eadaf7601290_xcz2kh.jpg"
    }
]
Enter fullscreen mode Exit fullscreen mode

Creating the concert ticket

With our project fully set up and configured, we can start building the concert ticket.

First, we need to modify the index.js file in the pages folder to the gist below:

From the code snippet in the gist, we:

  • Import required dependencies and image collections
  • Create state variables to manage selected Image ID, form data, and display the Concert ticket
  • A handleChange() function to control form inputs
  • 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

By now, the application should look like this:

concert generator

Next, we create a components folder in the root directory, and create a Concert.js file with the following snippet:

import { CloudinaryContext, Transformation, Image } from "cloudinary-react";

export const Concert = ({ message, name, publicId }) => {
  return (
    <div>
      <CloudinaryContext cloudName="beswift">
        <Image publicId={publicId} alt="image" width={1000} ref={ref}>
          <Transformation crop="fit" effect="blur:100" />
          <Transformation effect="brightness_hsb:-50" />
          <Transformation
            color="#FFFFFF"
            overlay={{
              background: "",
              fontFamily: "Neucha",
              fontSize: 150,
              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: `Artist: ${name}`,
            }}
          />
          <Transformation
            flags="layer_apply"
            gravity="center"
            x="450"
            y="350"
          />
        </Image>
      </CloudinaryContext>

    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

The snippet above does the following:

  • Imports the required dependencies
  • The Concert component accepts a message, name, and publicId props
  • Configure CloudinaryContext, Image, and Transformation to render the image, message, and name
  • We also leverage Cloudinary to apply multiple transformations on the image. We added the following transformations, cropping, blurring, brightening, and adding overlays for text, text position, text properties, and flags to alter the position of the text

Then, we update index.js to conditionally render the Concert.js component as shown below:

//imports here
import { Concert } from '../components/Concert'; //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'>Concert ticket</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'>
            <Concert
              message={formData.message}
              name={formData.name}
              publicId={formData.publicId}
            />
          </div>
        )}
      </main>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the snippet above, we:

  • Imported the Concert component
  • Created an handleSubmit function that checks if an image is selected and then conditionally renders the Concert component with necessary props

Storing data in the database

Let’s create a new file in the api folder and name it add-concert.js. Paste the code below in the file:

import { getXataClient } from "../../src/xata";
const handler = async (req, res) => {
  const xata = await getXataClient();
  const { concert_name, artist } =
    req.body;
  await xata.db.concert.create({
    concert_name,
    artist,
  });
  res.end();
};
export default handler;
Enter fullscreen mode Exit fullscreen mode

We get the data sent to the API and save it to the Xata database.

Finally, we test our application by starting the development server and creating different event tickets.

xata-cloudinary concert ticket app

We can right-click to copy the URL of the generated image.

Conclusion

This post discussed building a concert tickets generator using Cloudinary’s image transformation and saving data in the Xata database. Explore adding more text information to the image and a copy-to-clipboard function for the image URL.

Resources

We may find these resources helpful:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .