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:
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!
Responsive Gallery with Cloudinary
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.
You can use the available SDKs to upload images to your cloud or upload images manually via the dashboard:
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.
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.
Hoppe it’s stable!
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.
Well done man, thanks …