Telerik blogs
KendoReactT2_1200x303

In this article, we will build an image gallery with Next.js. We will be building this application with UI components from the KendoReact library and bringing in our images from Cloudinary.

In this post, we will be building an image gallery with Next.js and KendoReact. In a nutshell, we will build our Next.js application with components from the KendoReact library and bring in our image assets from Cloudinary.

This post assumes you are familiar with the basics of JavaScript and React.

Prerequisites

To follow this article, I recommend you have the following:

Cloudinary Setup

If you don’t have a Cloudinary account, you can sign up for a free account. Log in after creating your account, and on your dashboard page, you should see all your credentials (cloud name, etc.).

We will be using Cloudinary’s list delivery type to generate a JSON listing of the images we will use in this project. Cloudinary allows us to list resources from the client-side based on their tags, so we need to add resource tags to the assets (images) we want to use in our gallery.

When you assign tags to assets, you can perform group actions on them. Cloudinary will generate a JSON snippet containing all the images with that specified tag. Information such as its format, type, dimensions, contextual metadata and structured metadata will be returned for each image. Follow the steps below to add tags to your images:

  • First, you need to get all your images uploaded to your Cloudinary Media library.
  • Hover over one of your images, click on the more options menu (…) icon from the list of actions.
  • Click the Edit link.
  • Click the Add a tag link, and type in your desired name. I’ll be using “city.”
  • Repeat these steps to add tags to other images.

The URL syntax should look like this:

https://res.cloudinary.com/<your_cloud_name>/<resource_type>/list/<tag>.json

We will then query the URL to fetch a JSON list of all our images sharing the specified tag. Click here for more on adding tags to assets.

We also need to enable the image list delivery type because it is restricted by default. To enable it, click on the Security setting icon on your Cloudinary console. Click the Settings link on the security page and uncheck the Resource list option under Restricted media types.

Project Setup

Run the following commands to set up a Next.js project in a folder called image-gallery:

npx create-next-app image-gallery

To navigate to your application directory and get the app running, run the following command:

cd kendo-cloudinary-gallery
npm run dev

This should start up your project on your browser at localhost:3000.

Setting Up KendoReact License Key

KendoReact is distributed under a commercial license. The version I’m using (v4.10) supports using the KendoReact components without having or activating a license key just for development, but this fails during build. The steps to set up your license key are the same for paid members and those using the 30-day trial license.

Log in to your account and follow the steps here to download your license key. Copy the kendo-ui-license.txt license key file you just downloaded to the root of your project.

Finally, we need to install the KendoReact license as a project dependency and activate it. Run the following command in your terminal to do that:

npm install @progress/kendo-licensing
npx install kendo-ui-license activate

Install KendoReact Theme

KendoReact provides themes that we can use to style our application, and it currently ships five themes. Each theme includes a precompiled dist/all.css CSS file that contains the styles for all KendoReact components. We will be using the default theme in this project, so run the following command in your terminal to install its package:

npm install --save @progress/kendo-theme-default

We also need to include the theme in our project by referencing the dist/all.css in our App.js file like so:

// Add this import before your existing App.css import
import "@progress/kendo-theme-default/dist/all.css";

While we need to include one of these themes, KendoReact UI components are designed to allow us to change the way your theme looks by updating a Sass or CSS file, and we can also add custom styles to a CSS file.

Let’s update Home.module.css file with the following:

//styles/Home.modules.css
.container {
  margin-top: 10rem;
  min-height: 100vh;
  padding: 0 0.5rem;
}
.grid {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
}
@media (max-width: 600px) {
  .grid {
    width: 100%;
  }
}

KendoReact is a library of over 100 UI components published as several npm packages scooped to @progress. We need to install different packages for the components we will be using in our project. Run the following command in your terminal:

npm install @progress/kendo-react-buttons @progress/kendo-react-common @progress/kendo-react-layout @progress/kendo-react-dialogs

Back to the pages directory, let’s update our index.js file with the following:

// pages/index.js
import Head from "next/head";
import styles from "../styles/Home.module.css";
import CloudAssets from "../components/CloudAssets";
import { Typography } from "@progress/kendo-react-common";
export default function Home({ resData }) {
  return (
    <div className={styles.container}>
      <Head>
        <title>KendoUI Gallary </title>
        <meta name="description" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className="wrap k-align-items-center">
        <Typography.h2 textAlign={"center"} fontWeight={"bold"}>
          Awesome Gallary
        </Typography.h2>
        <Typography.p
          textAlign={"center"}
          themeColor={"inverse"}
          fontWeight={"light"}
        >
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus quam
          eos consequatur, <br /> rem ratione nesciunt quisquam dolorem,
          repudiandae officia totam amet corporis illum <br /> minus fugit
          incidunt magnam deserunt veniam dignissimos.
        </Typography.p>
        <CloudAssets data={resData} />
      </main>
    </div>
  );
}

export async function getStaticProps(context) {
  const res = await fetch(
    `https://res.cloudinary.com/ifeomaimoh/image/list/v1633911053/city.json`
  );
  const resData = await res.json();
  if (!resData) {
    return {
      notFound: true,
    };
  }
  return {
    props: { resData },
  };
}

In the code above, we’re also exporting getStaticProps() in the same file as the HomePage component, and it returns an object with props. We’re fetching our data in the getStaticProps() function from the URL we got from Cloudinary after tagging our images, and the response is returned in the props object, which will be passed to the Home component as props.

You can also see how we’re using and styling different variants of the Typography component included in the KendoReact Common package to display our content.

Run this command to start your development server and head over to http://localhost:3000/ in your browser.

npm run dev

Screenshot of basic Next.js application

Create a Card Image Component

Now let’s display the data we fetched from Cloudinary. Create a folder at the root of your project called components. Create a file called CloudAssets.js inside the folder and add the following to it:

components/CloudAssets.js
import styles from "../styles/Home.module.css";
import {
  Card,
  CardHeader,
  CardImage,
  CardTitle,
  CardBody,
  Avatar,
} from "@progress/kendo-react-layout";

const CloudAssets = ({ data }) => {
  const baseUrl = "https://res.cloudinary.com/ifeomaimoh/image";
  return (
    <>
      <div className={styles.grid}>
        {data &&
          data.resources.map((item) => {
            const { format, public_id, version, type } = item;
            return (
              <div
                style={{ padding: "10px" }}
                key={version}
              >
           <Card>
            <CardHeader className="k-hbox">
              <Avatar type="icon" size="small" shape="circle">
              <img
                src="https://a.storyblok.com/f/51376/x/da286b5766/cloudinary.svg"
                alt="avatar"
                width="45px"
                height="45px"
                />
                </Avatar>
                    <CardTitle
                      style={{
                        marginBottom: "4px",
                      }}>
                      Somewhere in London
                    </CardTitle>
                  </CardHeader>
                  <CardBody>
                    <CardImage
                    src={`${baseUrl}/${type}/v${version}/${public_id}.${format}`}
                      alt="first from cloud.."
                      width="420px"
                      height="300px"
                    />
                  </CardBody>
                </Card>
              </div>
            );
          })}
      </div>
    </>
  );
};
export default CloudAssets;

In the code above, we’re also pulling out the data we’re yet to pass as props to this component—CloudAssets. Remember the response returned in the props object when we used the getStaticProps() function to fetch our data from Cloudinary? That data is available as props and will be passed to this CloudAssets component. We are mapping over the data and using the KendoReact Card component and its contents, which are part of the KendoReact Layout package, to display our images.

We’re are also generating Cloudinary URLs for our images and passing them to the src attribute of the CardImage component.

Now, let’s import our CloudAssets component to our index.js file. Add the following to your index.js file:

//pages/index.js
import CloudAssets from "../components/CloudAssets";

Now we can render the component CloudAssets inside our Home component. Add the following to your Home component:

<main className="wrap k-align-items-center">
  ...
  <Typography.p
    textAlign={"center"}
    themeColor={"inverse"}
    fontWeight={"light"}
  >
    ... incidunt magnam deserunt veniam dignissimos.
  </Typography.p>
  <CloudAssets data={resData} />
</main>;

If you check your browser, your application should look like this:

Screenshot of image gallery

Our application already looks great, and this is only the tip of the iceberg compared to how much you can accomplish with Cloudinary and KendoReact.

That being said, let’s add another function to our app. Let’s make our images expand, showing a bigger image and a description whenever a user clicks on any of them.

Create a Modal Component

To achieve that, let’s create another file inside the components folder called Modal.js and add the following to it:

//components/Modal.js
import { Button } from "@progress/kendo-react-buttons";
import { Dialog, DialogActionsBar } from "@progress/kendo-react-dialogs";
import { Typography } from "@progress/kendo-react-common";
import {
  Card,
  CardHeader,
  CardTitle,
  CardBody,
  CardActions,
  CardImage,
  Avatar,
} from "@progress/kendo-react-layout";

function Modal({ baseUrl, data, setIsOpen }) {
  const { format, public_id, version, type } = data;
  const closeDialog = () => {
    setIsOpen(false);
  };
  return (
    <Dialog onClose={closeDialog} width={620} height={720}>
      <Card>
        <CardHeader
          style={{
            display: "flex",
            justifyContent: "space-between",
          }}
        >
          <Avatar type="icon" size="medium" shape="circle">
            <img
              src={`${baseUrl}/${type}/v${version}/${public_id}.${format}`}
              alt="dialog avatar"
              width="45px"
              height="45px"
            />
          </Avatar>
          <CardTitle>Somewhere in London</CardTitle>
          <CardActions>
            <Button primary={true} look="outline" onClick={closeDialog}>
              X
            </Button>
          </CardActions>
        </CardHeader>
        <CardBody>
          <CardImage
            src={`${baseUrl}/${type}/v${version}/${public_id}.${format}`}
            alt="dialog image"
            width="550"
            height="450"
          />
        </CardBody>
      </Card>
      <DialogActionsBar>
        <Typography.h3 margin={"xlarge"} padding={5}>
          Details: This Image is from{" "}
          <span>
            <a
              href="https://res.cloudinary.com/kizmelvin"
              target="_blank"
              rel="noreferrer"
            >
              Cloudinary
            </a>
          </span>
        </Typography.h3>
        <Typography.h3 margin={"xlarge"}>
          Credits:{" "}
          <span>
            <a href="https://unsplash.com/" target="_blank" rel="noreferrer">
              Unsplash
            </a>
          </span>
        </Typography.h3>
      </DialogActionsBar>
    </Dialog>
  );
}
export default Modal;

We’re using the KendoReact Dialog component in the code above, which is part of the KendoReact Dialogs package, to display a bigger size of our images and show additional information. It provides us a modal window, so whenever a user clicks on any image card, our Modal component receives the properties of that image as props and displays it. We’re also using the properties of the image to generate the image URL.

The closeDialog() function, as its name implies, is used to close the modal when a user clicks the close button.

Now let’s update our CloudAssets.js file to render the modal we just created. Replace whatever is in your CloudAssets.js file with the following:

//components/CloudAssets.js
import { useState } from "react";
import styles from "../styles/Home.module.css";
import Modal from "./Modal";
import {
  Card,
  CardHeader,
  CardImage,
  CardTitle,
  CardBody,
  Avatar,
} from "@progress/kendo-react-layout";

const CloudAssets = ({ data }) => {
  const [isOpen, setIsOpen] = useState(false);
  const [modalData, setModalData] = useState(null);
  const baseUrl = "https://res.cloudinary.com/ifeomaimoh/image";
  return (
    <>
      <div className={styles.grid}>
       {data &&
          data.resources.map((item) => {
            const { format, public_id, version, type } = item;
            return (
              <div
                style={{ padding: "10px" }}
                key={version}
                onClick={() => {
                  setIsOpen(true);
                  setModalData(item);
                }}
              >
          <Card>
            <CardHeader className="k-hbox">
              <Avatar type="icon" size="small" shape="circle">
                <img
                  src="https://a.storyblok.com/f/51376/x/da286b5766/cloudinary.svg"
                  alt="avatar"
                  width="45px"
                  height="45px"
                />
                    </Avatar>
                    <CardTitle
                      style={{
                        marginBottom: "4px",
                      }}
                    >
                      Somewhere in London
                    </CardTitle>
                  </CardHeader>
                  <CardBody>
                  <CardImage
                    src={`${baseUrl}/${type}/v${version}/${public_id}.${format}`}
                    alt="first from cloud.."
                    width="420px"
                    height="300px"
                  />
                  </CardBody>
                </Card>
              </div>
            );
          })}
      </div>
      {isOpen && (
        <Modal baseUrl={baseUrl} data={modalData} setIsOpen={setIsOpen} />
      )}
    </>
  );
};
export default CloudAssets;

We imported the Modal component and created two state variables, isOpen and modalData, using the useState() hook to keep track of which image is clicked. Initially, the value of isOpen is set to false. In the div containing each image card, we added an onClick handler to set the value of isOpen to true using the setIsOpen function when a user clicks on a card.

On click, we’re also calling the setModalData function to set the value of modalData to the properties of the image that was clicked. Then we’re conditionally rendering the Modal component only when the value or isOpen is equal to true.

Summary

In this post, we were able to set up KendoReact and Cloudinary successfully and demonstrated how to use them to build an image gallery with Next.js. With Cloudinary, we saw how easy it is to get our media assets and use them in our application. We also saw how easy it is to integrate KendoReact’s components into our application without worrying about writing long lines of CSS code. You can check out the KendoReact documentation for a complete list of amazing components you can use to build your application.


Ifeoma-Imoh
About the Author

Ifeoma Imoh

Ifeoma Imoh is a software developer and technical writer who is in love with all things JavaScript. Find her on Twitter or YouTube.

Related Posts

Comments

Comments are disabled in preview mode.