Codementor Events

Build a React Image Gallery with Cloudinary

Published Jan 13, 2017Last updated Jan 18, 2017
Build a React Image Gallery with Cloudinary

If you are displaying multiple images on your website, you probably use image galleries. Galleries are very useful, but can be challenging to integrate into a responsive design. In this article, we’ll show you how Cloudinary’s responsive transformation features enable you to quickly and easily create a responsive image gallery. The examples will be built with React.

Cloudinary Just announced a React SDK that enables you to easily build a responsive image gallery. You can simply drop an image from the cloud into your React app and apply transformations as config options.

Set up a React Environment

There are minimal requirements to set up React. Webpack, the de facto module bundling tool, needs to configured and React-related loaders added:

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

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

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

module.exports = config;

The process is very simple and straight to the point. We specify both the entry and output points, then tell Webpack to pass our JSX files through the babel loader.

Next, React, Cloudinary's React SDK, and other related dependencies, including Webpack and the respective loaders, must be installed:

./package.json

{
  "name": "img-gallery",
  "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"
  }
}

Now install the dependencies specified in the package.json above:

npm install

The axios library will assist us in making HTTP requests and handling the responses with promises. The custom scripts build, watch, and serve will be responsible for compiling the JSX source code using Webpack and serving them to the browser.

Few Babel presets were installed, so they need to be configured in the .babelrc file:

./.babelrc

{
  "presets" : ["es2015", "react"]
}

Create a React App

We already told Webpack that our app should start in the src folder with a file named index.js. Let's build a simple React app in this file and test it:

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

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

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

Before the app can be rendered, we need to create an entry index.html where the React compiled app will be rendered:

<!-- ./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>

Let's handle the styles now, instead of worrying about it later:

/* ./public/style.css */

/* Inspired by W3Schools */
body{
    background: #e1e1e1;
}
div.img {
    border: 1px solid #ccc;
}

div.img:hover {
    border: 1px solid #777;
}

div.img img {
    width: 100%;
    height: auto;
}

div.desc {
    padding: 15px;
    text-align: center;
}

* {
    box-sizing: border-box;
}

.responsive {
    padding: 0 6px;
    float: left;
    width: 24.99999%;
    margin-bottom: 10px;
}

@media only screen and (max-width: 700px){
    .responsive {
        width: 49.99999%;
        margin: 6px 0;
    }
}

@media only screen and (max-width: 500px){
    .responsive {
        width: 100%;
    }
}

.clearfix:after {
    content: "";
    display: table;
    clear: both;
}

.gallery{
    width: 80%;
    float: left;
}

.upload{
    width: 20%;
    float: left;
}

.container{
    padding-top: 50px;
}

h1{
    text-align: center;
}

.upload-button {
    padding: 10px 25px;
    font-size: 20px;
    color: brown;
    background-color: gold;
    border: 3px solid goldenrod;
}

.upload-button:hover{
    background-color: goldenrod;
    border: 3px solid gold;
}

.upload-button:active{
    outline: none;
}

Run npm build in your terminal and npm run serve in another terminal to test the app:

react image gallery

You can use npm run watch to build for every change made to the source files.

The most significant content in the styles is where we are using media queries to make each image full width for small devices and x by x of the width for larger devices. Simple stuff!

After setting up React and testing that everything works as expected, let's see how we can employ Cloudinary, a cloud-based media management solution that provides storage, transformation, and delivery.

To get a private cloud, you need to sign up for free, and note your cloud name and credentials as seen on the dashboard.

react image gallery

You can use the available SDKs to upload images to your cloud or upload images manually via the dashboard:

react image gallery

As you can see, I have uploaded some Christmas images I got from Pexels.

Configure Cloudinary with Cloudinary Context

Moving forward, we need to configure the installed Cloudinary SDK with the cloud_name:

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

class Home extends Component {
   
    render(){
        return (
            <div className="main">
                <h1>Galleria</h1>
                <div className="gallery">
                    <CloudinaryContext cloudName="cloud_name">
                        
                    </CloudinaryContext>
                    <div className="clearfix"></div>
                </div>
            </div>
        );
    }
}

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

The CloudContext component is used to create a config context that would wrap a group of images and image transformations. In that case, any image component or image transformation component added inside this context will inherit the configuration so there would be no need to explicitly specify another.

Add Images to Cloudinary Context

With a context created and configured, we need to start adding images:

// ...
render() {
        return (
            <div className="main">
                <h1>Galleria</h1>
                <div className="gallery">
                    <CloudinaryContext cloudName="cloud_name">
                        <div className="responsive">
                            <div className="img">
                                <a target="_blank" href={`http://res.cloudinary.com/christekh/image/upload/christmas.jpg`}>
                                    <Image publicId={'christmas.jpg'}>
                                                         </Image>
                                </a>
                            </div>
                        </div>
                                )
                            })
                        }
                    </CloudinaryContext>
                    <div className="clearfix"></div>
                </div>
            </div>
        );
    }

The Image component accepts a publicId prop that defines which image should be loaded by the component. This id must be valid and represent a given image in your cloud.

You can request multiple images and display them by iterating through the results:

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

class Home extends Component {
    constructor(props) {
        super(props);
        this.state = {
            gallery: []
        }
    }
    componentDidMount(){
        axios.get('http://res.cloudinary.com/christekh/image/list/xmas.json')
            .then(res => {
                console.log(res.data.resources);
                this.setState({gallery: res.data.resources});
            });
    }
    
    render(){
        return (
            <div className="main">
                <h1>Galleria</h1>
                <div className="gallery">
                    <CloudinaryContext cloudName="christekh">
                        {
                            this.state.gallery.map(data => {
                                return (
                                    <div className="responsive" key={data.public_id}>
                                        <div className="img">
                                            <a target="_blank" href={`http://res.cloudinary.com/christekh/image/upload/${data.public_id}.jpg`}>
                                                <Image publicId={data.public_id}>
                                                  
                                                </Image>
                                            </a>
                                            <div className="desc">Created at {data.created_at}</div>
                                        </div>
                                    </div>
                                )
                            })
                        }
                    </CloudinaryContext>
                    <div className="clearfix"></div>
                </div>
            </div>

        );
    }
}

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

We first create a gallery state, which is set to a collection of image data after the component gets mounted and axios is used to ask the cloud for a collection of images. xmas in xmas.json at the tail of the request URL represents images grouped under the xmas tag and that is what we want.

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

Responsive Transformation

The React Cloudinary SDK also provides a Transformation component that gives you the ability to apply transformation declaratively as props:

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

class Home extends Component {
    constructor(props) {
        super(props);
        this.state = {
            gallery: []
        }
    }
    componentDidMount(){
        axios.get('http://res.cloudinary.com/christekh/image/list/xmas.json')
            .then(res => {
                console.log(res.data.resources);
                this.setState({gallery: res.data.resources});
            });
    }
    render(){
        return (
            <div className="main">
                <h1>Galleria</h1>
                <div className="gallery">
                    <CloudinaryContext cloudName="christekh">
                        {
                            this.state.gallery.map(data => {
                                return (
                                    <div className="responsive" key={data.public_id}>
                                        <div className="img">
                                            <a target="_blank" href={`http://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(<Home />, document.getElementById('container'));

The width, crop scale, and dpr properties are provided by Cloudinary to help you adjust images to make them fit your responsive design while still retaining the image quality.

react image gallery
react image gallery

Conclusion

With few lines of codes, we were able to add a responsive image gallery to a React app. Cloudinary saves you the stress of managing images especially when the image needs to be transformed as well as stored on a reliable server. The Cloudinary React SDK makes our work easier and code cleaner by abstracting the need to do a fetch directly to your Cloudinary server so as to retrieve images and transform them.

Discover and read more posts from Christian Nwamba
get started
post commentsBe the first to share your opinion
vaiiho nascimbene
4 years ago

Hoppe it’s stable!

sugandh goyal
6 years ago

Hi, my image gallery does not show up. Neither it gives me an error. Can anyone help? It does not even show a single image with <Image> tag. Please help me.

teddy kekana
7 years ago

Well done man, thanks …

Show more replies