Skip to content

Leveraging React’s Compiler With Cloudinary for Optimized Image Handling

React’s new compiler introduces a groundbreaking advancement in performance optimization by automatically memoizing components and hooks during the build process. This reduces unnecessary re-computations, enhancing the efficiency of updates. Memoization is crucial for optimizing performance, especially in applications with heavy rendering requirements. In this blog post, we’ll explore React’s new compiler through a sample project featuring a card-flipping animation, where we’ll see the benefits of automatic memoization in action using React DevTools.

To fully benefit from this blog post, you should have a basic understanding of:

  • JavaScript. Essential for writing the logic in our React components.
  • React.js. Knowledge of React’s component-based architecture.
  • Tailwind CSS. Familiarity with utility-first CSS for styling components.
  • Next.js 14 app router. The latest Next.js routing and data fetching methods.
  • Cloudinary. Experience with image management and transformations using Cloudinary.

The React compiler optimizes the performance of React applications by automatically memoizing code. This means that React can skip re-rendering parts of the application that haven’t changed, reducing the computational load during updates. Traditionally, developers use hooks like useMemo, useCallback, and React.memo to achieve this, but React’s new compiler does this automatically, ensuring optimal performance without the need for manual intervention.

Note:

React does state on their docs that this is experimental and a WIP.  However, Facebook and Meta have been using this for a while now internally, so it’s probably close to being capable of shipping into production applications.  Just use it with caution.  

It also works with plain JavaScript, and understands the Rules of React, so you don’t need to rewrite any code to use it.

Let’s start by setting up our Next.js project with the canary branch for React compiler and integrating Tailwind CSS for styling. We’ll build a sample project that demonstrates the card-flipping animation using images from Cloudinary.

Go to your terminal and run:

npm install next@canary babel-plugin-react-compilerCode language: CSS (css)

Then configure the experimental option in next.config.js at your projects root:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};

module.exports = nextConfig;
Code language: JavaScript (javascript)

Since Tailwind comes in the Next.js 14 package, you don’t have to install it.  Simply edit the tailwind.config.js file in the root of your application like so:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      backgroundImage: {
        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
        "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
      },
      transitionDuration: {
        '600': '600ms',
      },
      rotate: {
        'y-180': 'rotateY(180deg)',
      },
      transformStyle: {
        'preserve-3d': 'preserve-3d',
      },
      backfaceVisibility: {
        'hidden': 'hidden',
      },
    },
  },
  plugins: [],
};
Code language: JavaScript (javascript)

In the root of the app folder, navigate to your globals.css file and edit to add this:

@tailwind base;
@tailwind components;
@tailwind utilities;

.perspective {
  perspective: 1000px;
}

.backface-hidden {
  backface-visibility: hidden;
}

.transform-style-preserve-3d {
  transform-style: preserve-3d;
}

.rotate-y-180 {
  transform: rotateY(180deg);
}

/* Set background color of all pages to navy blue */
body {
  background-color: navy;
  color: white; /* Ensuring text is readable on a dark background */
}
Code language: CSS (css)

The Cloudinary team has made it seamless and easy to install their dependency into Next.js. Go to your terminal and add this command:

`npm install cloudinary`Code language: JavaScript (javascript)

This is the Cloudinary Node SDK, which allows you to quickly and easily integrate your application with Cloudinary. It will help optimize, transform, upload, and manage cloud’s assets.

Once done, go to the root of your Next.js project and in your next.config.js file, add this edit to allow Cloudinary images:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
  images: {
    domains: ["res.cloudinary.com"],
  },
};

export default nextConfig;
Code language: JavaScript (javascript)

Just like that, we’re all set up to process public images from your Cloudinary account.

The Card.js file will be the component that will handle the flipping logic using state to toggle between the front and back images.  Create a components folder in the root of the app directory and add this code block:

"use client";

import React, { useState } from "react";
import PropTypes from "prop-types";
import Image from "next/image";

const Card = ({ frontImageId, backImageId }) => {
  const [isFlipped, setIsFlipped] = useState(false);

  const handleClick = () => {
    setIsFlipped(!isFlipped);
  };

  return (
    <div className="w-64 h-64 perspective" onClick={handleClick}>
      <div className={`relative w-full h-full duration-600 transform-style-preserve-3d ${isFlipped ? "rotate-y-180" : ""}`}>
        <div className="absolute w-full h-full backface-hidden">
          <Image
            src={`https://res.cloudinary.com/your-cloud-name/image/upload/${frontImageId}`}
            alt="Front Image"
            width={256}
            height={256}
            className="object-cover rounded-lg"
          />
        </div>
        <div className="absolute w-full h-full rotate-y-180 backface-hidden">
          <Image
            src={`https://res.cloudinary.com/your-cloud-name/image/upload/${backImageId}`}
            alt="Back Image"
            width={256}
            height={256}
            className="object-cover rounded-lg"
          />
        </div>
      </div>
    </div>
  );
};

Card.propTypes = {
  frontImageId: PropTypes.string.isRequired,
  backImageId: PropTypes.string.isRequired,
};

export default Card;
Code language: JavaScript (javascript)

At the top of the file, we include the “use client” directive to inform Next.js that this is a client-side component. This is necessary because Next.js 14 defaults to server components. We’ll then import useState from React to manage the flipping state of the card

Within the Card component, we’ll use the useState hook to create a boolean state variable, isFlipped, which toggles between true and false when the card is clicked. The handleClick function toggles this state, effectively flipping the card.

The component structure involves a parent div with a CSS class for perspective, ensuring the 3D effect is visible. Inside this, we conditionally apply the rotate-y-180 class based on the isFlipped state to perform the flip animation.

For the images, we’ll use the Image component from Next.js, setting the src attribute to the Cloudinary URL, which includes your Cloudinary cloud name. Make sure to replace “your-cloud-name” with your actual Cloudinary cloud name obtained from the Cloudinary dashboard. Each Image component has defined dimensions and applies Tailwind CSS classes for styling.

Lastly, we’ll define propTypes to ensure frontImageId and backImageId are required string props, validating that the necessary data is passed to the component. This helps maintain type safety and ensures the component functions correctly.

The ImageGallery component will display multiple Card components, each capable of flipping between two images.

import React from "react";
import Card from "./Card";

const ImageGallery = () => {
  const images = [
    {
      front: "sample",
      back: "kenny-eliason-5lkoseqWobM-unsplash",
    },
    { front: "sample", back: "wpgraphql" },
    { front: "sample", back: "card_8_rulqmw" },
  ];

  return (
    <div className="flex flex-row justify-center space-x-8">
      {images.map((image, index) => (
        <div key={index} className="flex flex-col items-center space-y-2">
          <h2 className="text-lg font-semibold">Image {index + 1}</h2>
          <Card frontImageId={image.front} backImageId={image.back} />
        </div>
      ))}
    </div>
  );
};

export default ImageGallery;
Code language: JavaScript (javascript)

We have a React server component here that takes in the variables of images from your Cloudinary account. This is an array with the object that contains the hard-coded names from your Cloudinary image file names. In this case, I’ll use my own, but feel free to swap those out in your own code.  

We’re all set and good to go.  Let’s run the project on the dev server.  In the terminal, run npm run dev, open your browser, and navigate to http://localhost:3000. You should see a page like this:

In the above image, all three start with the flower image, but upon clicking the image, you should be able to flip it and reveal the backside of the image, which in my case is a truck and the WPGraphQL symbol.
The next step is to check how React auto memoizes components.  If you don’t have React Dev Tools installed, please do so now.  I am using Google Chrome’s extension. Now that you have it installed, open the Dev Tools in the browser. If you’re on a Mac, it’s CMB + Option + I:

Now, go to the top of the toolbar right next to Sources. There are two arrows on top of each other. Click that to reveal this dropdown:

When you reveal the dropdown, click Components.  Scroll down to the Card section and you should see the word “Memo next to it!

What’s happening is the Card components are memoized without explicitly using React.memo.

Memoization ensures that the Card components dont re-render unnecessarily, which can be verified by observing the components’ props and state in React DevTools. The new React compiler optimizes these re-renders, improving performance significantly.  And we didn’t have to do anything except install the new version and the canary!  

React’s new compiler introduces automatic memoization, reducing the need for manual optimizations with hooks like useMemo and React.memo. By integrating Tailwind CSS for styling and Cloudinary for image handling, we demonstrated a practical example of a card-flipping animation. 

The use of React DevTools further highlights the benefits of automatic memoization, ensuring our components are optimized for performance without additional code. This setup not only enhances performance but also simplifies the development process, making it easier to build efficient and responsive React applications.

Build your next React project using Cloudinary’s extensive image transformations. Sign up for free today. And if you found this blog post helpful and want to discuss it in more detail, head over to the Cloudinary Community forum and its associated Discord.

Back to top

Featured Post