Cloudinary Blog

How to Build an Image Library with React & Cloudinary

Build an Image Library with React & Cloudinary

This article was originally posted on Scotch.io

React is a good tool when it comes to building flexible and reusable UI components. However, it's "one of those libraries" that cannot handle all the tasks involved in building a full fleshed UI project. Other supporting tools - such as a recently announced React SDK from Cloudinary - are available to provide solutions that the React core cannot.

In such cases where media (images and videos) becomes a heavy task to handle, Cloudinary simplifies the process with the new React SDK. Let's build and image library with Cloudinary and React using the Cloudinary React SDK.

Prerequisites

The only requirements for using Cloudinary in your existing React project are to install the React SDK and the upload widget. If you do not have an existing React project and want to try these examples, take the following steps:

1. Install Dependencies

We need a minimal amount of dependencies so we can focus on building a media library and not structuring a React app:

{
  "name": "img-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "watch": "webpack -d --watch",
    "build": "webpack",
    "serve": "serve ./public"
  },
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.9",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "serve": "^1.4.0",
    "webpack": "^1.14.0"
  },
  "dependencies": {
    "axios": "^0.15.3",
    "cloudinary-react": "^1.0.1",
    "react": "^15.4.1",
    "react-dom": "^15.4.1"
  }
}

React (and React DOM) must be used since we are making a React app. The cloudinary-react dependency is Cloudinary's React SDK, which we will soon see how it works. axios is a tool for making HTTP requests and, in our case, we will use it request images from the Cloudinary server.

# Install dependencies
npm install

2. Setup Webpack

Webpack is our build tool. Only minimal settings are required to have a build running and our React app compiling:

// ./webpack.config.js
var webpack = require('webpack');
var path = require('path');

var BUILD_DIR = path.resolve(__dirname, 'public');
var APP_DIR = path.resolve(__dirname, 'src');

var config = {
    entry: APP_DIR + '/index.jsx',
    output: {
        path: BUILD_DIR,
        filename: 'bundle.js'
    },
    module : {
        loaders : [
            {
                test : /\.jsx?/,
                include : APP_DIR,
                loader : 'babel'
            }
        ]
    }
};

module.exports = config;

Basic configuration -- an entry, output and loaders to handle the React .jsx files.

3. Entry Points

We need to create an entry point, as we specified in the Webpack configuration, and another entry point for the browser, which is an index.html file:

// ./src/index.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';

class Main extends Component {
    render() {
        return (
           <div className="main">
               <h1>Scotchage</h1>
           </div>
        );
    }
}

render(<Main />, document.getElementById('container'));
<!-- ./public/index.html -->
<html>
<head>
    <!--Stylesheet-->
    <link rel="stylesheet" href="style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <!--Container for React rendering-->
    <div id="container"></div>
    <!--Bundled file-->
    <script src="bundle.js"></script>
</body>
</html>

4. Create Cloudinary Account

You need a Cloudinary account to continue with these examples. Sign up for free and store your credentials safely as shown on the dashboard:

Uploading Images

Before using the React SDK to deliver images from the Cloudinary servers, let's use the awesome Cloudinary upload widget to upload images. First, we need to add this widget to our index.html:

<!-- ./public/index.html -->
<html>
<head>
   . . .
</head>
<body>
    . . .
    <!-- UPLOAD WIDGET -->
    <script src="//widget.cloudinary.com/global/all.js" type="text/javascript"></script>
    <script src="bundle.js"></script>
</body>
</html>

Next, we create a button, attach an event to it and try to upload an image once the button is clicked:

import React, { Component } from 'react';
import { render } from 'react-dom';

class Main extends Component {

    uploadWidget() {
        cloudinary.openUploadWidget({ cloud_name: 'CLOUD_NAME', upload_preset: 'PRESET', tags:['xmas']},
            function(error, result) {
                console.log(result);
            });
    }
    render(){
        return (
            <div className="main">
                <h1>Galleria</h1>
                <div className="upload">
                    <button onClick={this.uploadWidget.bind(this)} className="upload-button">
                        Add Image
                    </button>
                </div>
            </div>

        );
    }
}

render(<Main />, document.getElementById('container'));

The uploadWidget member method is the handler invoked by the click event to handle our image upload by calling cloudinary.openUploadWidget. openUploadWidget takes a config object and the upload callback handler. The config object must have at least cloud_name and upload_preset properties with valid values. You can read more about Cloud Names and Upload Presets.

Delivering Images with SDK

The Cloudinary React SDK has three major components, Image, CloudinaryContext and Transformation:

  • Image: This component is responsible for the actual delivery of images. It takes the image ID and asks the server for this image. When the image is provided, it is also responsible for painting the image on the browser.
  • Transformation: This component is used to apply transformations to images delivered with Image.
  • CloudinaryContext: You can specify Cloudinary configuration for each image on the Image component. This can be tedious when you are dealing with multiple images. CloudinaryContext allows you to apply configuration to a group of Images.

Most times you would end up with a structure like this:

<CloudinaryContext>
    <Image>
        <Transformation />
        <Transformation />
    </Image>
    <Image>
        <Transformation />
    </Image>
</CloudinaryContext>

Back to our demo app, we can request an image from the Cloudinary server and display it with the following components:

import React, { Component } from 'react';
import axios from 'axios';
import { CloudinaryContext, Transformation, Image } from 'cloudinary-react';
import { render } from 'react-dom';

class Main extends Component {
    constructor(props) {
        super(props);
        this.state = {
            gallery: []
        }
    }
    componentDidMount() {
        // Request for images tagged xmas       
        axios.get('https://res.cloudinary.com/christekh/image/list/xmas.json')
            .then(res => {
                console.log(res.data.resources);
                this.setState({gallery: res.data.resources});
            });
    }
    uploadWidget() {
       // . . .
    }
    render(){
        return (
            <div className="main">
                <h1>Galleria</h1>
                <div className="gallery">
                    <CloudinaryContext cloudName="CLOUDNAME">
                        {
                            this.state.gallery.map(data => {
                                return (
                                    <div className="responsive" key={data.public_id}>
                                        <div className="img">
                                            <a target="_blank" href={`https://res.cloudinary.com/christekh/image/upload/${data.public_id}.jpg`}>
                                                <Image publicId={data.public_id}>
                                                    <Transformation
                                                        crop="scale"
                                                        width="300"
                                                        height="200"
                                                        dpr="auto"
                                                        responsive_placeholder="blank"
                                                    />
                                                </Image>
                                            </a>
                                            <div className="desc">Created at {data.created_at}</div>
                                        </div>
                                    </div>
                                )
                            })
                        }
                    </CloudinaryContext>
                    <div className="clearfix"></div>
                </div>
            </div>

        );
    }
}

render(<Main />, document.getElementById('container'));

Take one more look at the upload code:

 cloudinary.openUploadWidget({ cloud_name: 'christekh', upload_preset: 'idcidr0h', tags:['xmas']},
            function(error, result) {
            . . .

Each image is tagged with xmas, which serves as a way to request images with this tag as a collection. This is exactly what we are using the axios library to do when the component mounts:

axios.get('https://res.cloudinary.com/CLOUDNAME/image/list/xmas.json')
            .then(res => {
                console.log(res.data.resources);
                this.setState({gallery: res.data.resources});
            });

axios uses promises, so whenever the promise resolves in our case, we have a payload of images. We take advantage of React state to update our UI with the fetched resources.

Down to rendering, we configure the CloudinaryContext with our cloud_name, iterate over the gallery state that stores the images and displays them using the Image component. We also apply few transformations using the Transformation component.

For security reasons, Cloudinary will not allow you to make such request from the client unless you tell it to. The best way to go is to use the admin API via a backend SDK and then send the resource list to the client.

Updating State with New Uploads

We are able to upload images and request for images to be displayed on the user's browsers. Here is how we update the displayed images instantly when the user uploads a new image:

uploadWidget() {
        let _this = this;
        cloudinary.openUploadWidget({ cloud_name: 'CLOUDNAME', upload_preset: 'PRESET', tags:['xmas']},
            function(error, result) {
            // Update gallery state with newly uploaded image
                _this.setState({gallery: _this.state.gallery.concat(result)})
            });
    }

Rather than logging the uploaded image information to the console, we update the gallery state, which bears the list of requested images, by concatenating the uploaded result to the gallery.

Image Management Simplified

Image uploads, transformation and delivery has never been easier. These tasks have been a serious challenge for developers. Cloudinary has created a way to abstract all this hard work, enabling you to simply plug and play.

Christian Nwamba Christian Nwamba (CodeBeast), is a JavaScript Preacher, Community Builder and Developer Evangelist. In his next life, Chris hopes to remain a computer programmer.

Recent Blog Posts

Build a WhatsApp Clone with Automatic Image Optimization

In the previous post, we showed how to upload images to a Cloudinary server. In this part, we will play with some of the features we see on the WhatsApp technology. After you or your users have uploaded image assets to Cloudinary, you can deliver them via dynamic URLs. You can include instructions in your dynamic URLs that tell Cloudinary to manipulate your assets using a set of transformation parameters. All image manipulations and image optimizations are performed automatically in the cloud and your transformed assets are automatically optimized before they are routed through a fast CDN to the end user for an optimal user experience. For example, you can resize and crop, add overlays, blur or pixelate faces, apply a variety of special effects and filters, and apply settings to optimize your images and to deliver them responsively.

Read more
With automatic video subtitles, silence speaks volumes

The last time you scrolled through the feed on your favorite social site, chances are that some videos caught your attention, and chances are, they were playing silently.

On the other hand, what was your reaction the last time you opened a web page and a video unexpectedly began playing with sound? If you are anything like me, the first thing you did was to quickly hunt for the fastest way to pause the video, mute the sound, or close the page entirely, especially if you were in a public place at the time.

Read more
Impressed by WhatsApp Tech? Build WhatsApp Clone with Media Upload

With more than one billion people using WhatsApp, the platform is becoming a go-to for reliable and secure instant messaging. Having so many users means that data transfer processes must be optimized and scalable across all platforms. WhatsApp is touted for its ability to achieve significant media quality preservation when traversing the network from sender to receiver, and this is no easy feat to achieve.

Read more
New Google-powered add-on for auto video categories and tags

Due to significant growth of the web and improvements in network bandwidth, video is now a major source of information and entertainment shared over the internet. As a developer or asset manager, making corporate videos available for viewing, not to mention user-uploaded videos, means you also need a way to categorize them according to their content and make your video library searchable. Most systems end up organizing their video by metadata like the filename, or with user-generated tags (e.g., youtube). This sort of indexing method is subjective, inconsistent, time-consuming, incomplete and superficial.

Read more

iOS Developer Camp: The Dog House

By Shantini Vyas
iOS Developer Camp: The Dog House

Confession: I’m kind of addicted to hackathons. Ever since graduating from Coding Dojo earlier this year, I’ve been on the hunt for new places to expand my skills and meet new people in the tech space. iOS Developer Camp’s 10th Anniversary event bowled me over. Initially, because of its length. 48 hours? Yeesh. I had no idea that those 48 hours would change my life. But let’s first get a little backstory on my favorite topic: dogs.

Read more