Geek Culture
Published in

Geek Culture

Build a Cloudinary Image Uploader with ReactJS and NodeJS

Cloudinary
Initialization
Remove
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './base.css';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
:root {
--text-color: #333;
--accent-color: rgb(15, 73, 54);
--btn-text-color: #f9f9f9;
--alert-success-bg-color: #d4edda;
--alert-success-text-color: #073813;
--alert-danger-bg-color: #dbb1b6;
--alert-danger-text-color: #3a070c;
}
* {
box-sizing: border-box;
color: var(--text-color);
}
.container {
margin: 0 auto;
max-width: 1200px;
padding: 2rem;
}
img {
border-radius: 5px;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-brand {
font-size: 24px;
}
.nav-items {
list-style: none;
display: flex;
}
.nav-item > a {
margin: 0 1rem;
text-decoration: none;
font-size: 16px;
color: #333;
padding-bottom: 2px;
font-weight: 500;
text-transform: uppercase;
}
.nav-item > a:hover {
border-bottom: 4px solid var(--accent-color);
}
.title {
text-align: center;
margin-bottom: 4rem;
}
.btn {
padding: 0.5rem 1rem;
border: 1px solid var(--accent-color);
background-color: var(--accent-color);
color: var(--btn-text-color);
border-radius: 5px;
transition: 250ms;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
border: 1px solid var(--accent-color);
background-color: var(--btn-text-color);
color: var(--acent-color);
}
.form {
margin-bottom: 2rem;
}
.form-input {
display: block;
margin-bottom: 10px;
}
.alert {
padding: 1rem 2rem;
border-radius: 5px;
position: fixed;
top: 2rem;
right: 0rem;
min-width: 200px;
}
.alert-success {
background-color: var(--alert-success-bg-color);
color: var(--alert-success-text-color);
}
.alert-danger {
background-color: var(--alert-danger-bg-color);
color: var(--alert-danger-text-color);
}
npm i react-router-dom
import React from 'react';
import './App.css';
import { BrowserRouter as Router, Link, Switch, Route } from 'react-router-dom';
import Upload from './pages/Upload.js';
import Home from './pages/Home.js';
function App() {
return (
<div className="container">
<Router>
<nav className="nav">
<div className="nav-brand">Cloudinary React</div>
<ul className="nav-items">
<li className="nav-item">
<Link to="/">Gallery</Link>
</li>
<li className="nav-item">
<Link to="/upload">Upload</Link>
</li>
</ul>
</nav>
<Switch>
<Route component={Upload} path="/upload" />
<Route component={Home} path="/" />
</Switch>
</Router>
</div>
);
}
export default App;
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, 300px);
grid-gap: 2rem;
justify-content: center;
}
@media (max-width: 786px) {
.gallery {
grid-template-columns: 1fr;
justify-items: center;
}
}
import React from 'react'const Home = () => {
return (
<div>
<h1>Home</h1>
</div>
)
}
export default Home
import React from 'react'const Upload = () => {
return (
<div>
<h1>Upload</h1>
</div>
)
}
export default Upload
localhost
import React, { useState } from 'react'const Upload = () => {
const [fileInputState, setFileInputState] = useState('');
const [previewSource, setPreviewSource] = useState('');
const handleFileInputChange = (e) => {
const file = e.target.files[0];
previewFile(file);
setFileInputState(e.target.value);
};
const previewFile = (file) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setPreviewSource(reader.result);
};
};
return (
<div>
<h1>Upload</h1>
<form>
<input
id="fileInput"
type="file"
name="image"
onChange={handleFileInputChange}
value={fileInputState}
className="form-input"
/>
<button className="btn" type="submit">
Submit
</button>
</form>
{previewSource && (
<img
src={previewSource}
alt="chosen"
style={{ height: '300px' }}
/>
)}
</div>
)
}
export default Upload
Nice Preview
<form onSubmit={handleSubmitFile} className="form">
Upload.js
package.json
npm init
npm i cloudinary cors dotenv express nodemon
const express = require('express');
const app = express();
var cors = require('cors');
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(cors());
app.post('/api/upload', async (req, res) => {
try {
const fileStr = req.body.data;
console.log(fileStr);
} catch (err) {
console.error(err);
res.status(500).json({ err: 'Something went wrong' });
}
});
const port = process.env.PORT || 3001;
app.listen(port, () => {
console.log('listening on 3001');
});
nodemon
require('dotenv').config();const cloudinary = require('cloudinary').v2;cloudinary.config({
cloud_name: process.env.CLOUDINARY_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
module.exports = { cloudinary };
Cloudinary
CLOUDINARY_API_KEY=4xxxxxxxxxxxxxx9
CLOUDINARY_API_SECRET=rxxxxxxxxxxxxxxxxxxxxxxxxs
CLOUDINARY_NAME=dxxxxxxxxxo
Upload
Save
New presets
server.js
Cloudinary image
server.js
All images
npm i cloudinary-react
import React, { useEffect, useState } from 'react';
import { Image } from 'cloudinary-react';
export default function Home() {
const [imageIds, setImageIds] = useState();
const loadImages = async () => {
try {
const res = await fetch('/api/images');
const data = await res.json();
setImageIds(data);
} catch (err) {
console.error(err);
}
};
useEffect(() => {
loadImages();
}, []);
return (
<div>
<h1 className="title">Cloudinary Gallery</h1>
<div className="gallery">
{imageIds &&
imageIds.map((imageId, index) => (
<Image
key={index}
cloudName={process.env.REACT_APP_CLOUDINARY_NAME}
publicId={imageId}
width="300"
height="200"
crop="scale"
/>
))}
</div>
</div>
);
}
REACT_APP_CLOUDINARY_NAME=dxxxxxxxo
.gitignore
.gitignore
App complete

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store