Build An E-commerce App Using React and Cloudinary

Nwokocha Wisdom Maduabuchi

Introduction:

As businesses keep moving to have an online presence, there is an increase in the e-commerce application built, it is important for these businesses to manage their media content properly. One of the ways to do that is by uploading your videos and images to Cloudinary. In this tutorial, we will learn how to upload images to Cloudinary from our admin page and at the same time to our product page by building an e-commerce site.

Prerequisite:

  • An understanding of React.js.
  • Installation of React Cloudinary SDK.
  • Installation of React Bootstrap.
  • Installation of Axios.
  • Installation of React Router.
  • Heroku installation.

Step One: Creating A React App

  • To start the project create a react app using the following command.
1npx create-react-app cloud commerce
  • Open up the react project on your code editor.
  • Click on the terminal and install the React Cloudinary SDK as shown below.
1npm i @cloudinary/url-gen @cloudinary/react
  • You are now ready to begin your project.

  • React is made up of reusable pieces of UI called Component, unlike HTML where you have the header, main body, and footer on the same page. In react you will create them separately as Components. You will create a folder called Components, this will contain the following folders; the header folder that contains the header.jsx file and its CSS.

  • The layout folder contains the layout.jsx and its CSS, this cont ains the main body.

  • The footer folder contains the footer.jsx and its CSS. The product folder contains the footer.jsx and its CSS. The superAdmin folder contains the footer.jsx and its CSS.

  • We will be focusing more on the layout, super admin, and product file.

  • You will create the layout page using React Bootstrap card element, install the React Bootstrap as shown below.

1npm install react-bootstrap bootstrap@5.1.3

https://github.com/wise4rmgod/Cloudinray-Ecommercesite

  • Import the card into the layout component.The card element will contain an image, the title, the product description, and the price.
  • To get the data, Install the JSON server,
1npm i json-server
  • You will create a mock JSON server as seen below.
1{
2 "productData": [
3 {
4 "id": 1,
5 "image": "https://images.pexels.com/photos/3782789/pexels-photo-3782789.jpeg?cs=srgb&dl=pexels-bella-zhong-3782789.jpg&fm=jpg",
6 "title": "Beautiful",
7 "product": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
8 "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus arcu felis bibendum ut tristique et egestas. Proin libero nunc consequat interdum. Nulla facilisi etiam dignissim diam quis enim. Cum sociis natoque penatibus et magnis. Quam pellentesque nec nam aliquam. Viverra orci sagittis eu volutpat odio facilisis mauris sit amet. Lorem ipsum dolor sit amet consectetur adipiscing. Bibendum est ultricies integer quis auctor elit.",
9 "price": 80000
10 },
11 {
12 "id": 2,
13 "image": "https://res.cloudinary.com/pueneh/image/upload/v1644066944/qriwggq3qf5xfd1on7qd.jpg",
14 "title": "Beautiful",
15 "product": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
16 "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus arcu felis bibendum ut tristique et egestas. Proin libero nunc consequat interdum. Nulla facilisi etiam dignissim diam quis enim. Cum sociis natoque penatibus et magnis. Quam pellentesque nec nam aliquam. Viverra orci sagittis eu volutpat odio facilisis mauris sit amet. Lorem ipsum dolor sit amet consectetur adipiscing. Bibendum est ultricies integer quis auctor elit.",
17 "price": 80000
18 },
19 {
20 "id": 3,
21 "image": "https://images.pexels.com/photos/3602449/pexels-photo-3602449.jpeg?cs=srgb&dl=pexels-bella-zhong-3602449.jpg&fm=jpg",
22 "title": "Beautiful",
23 "product": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
24 "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus arcu felis bibendum ut tristique et egestas. Proin libero nunc consequat interdum. Nulla facilisi etiam dignissim diam quis enim. Cum sociis natoque penatibus et magnis. Quam pellentesque nec nam aliquam. Viverra orci sagittis eu volutpat odio facilisis mauris sit amet. Lorem ipsum dolor sit amet consectetur adipiscing. Bibendum est ultricies integer quis auctor elit.",
25 "price": 80000
26 },
27 {
28 "id": 4,
29 "image": "https://images.pexels.com/photos/2529148/pexels-photo-2529148.jpeg?cs=srgb&dl=pexels-melvin-buezo-2529148.jpg&fm=jpg",
30 "title": "Beautiful",
31 "product": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
32 "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus arcu felis bibendum ut tristique et egestas. Proin libero nunc consequat interdum. Nulla facilisi etiam dignissim diam quis enim. Cum sociis natoque penatibus et magnis. Quam pellentesque nec nam aliquam. Viverra orci sagittis eu volutpat odio facilisis mauris sit amet. Lorem ipsum dolor sit amet consectetur adipiscing. Bibendum est ultricies integer quis auctor elit.",
33 "price": 80000
34 },
35 {
36 "id": 5,
37 "image": "https://images.pexels.com/photos/3076787/pexels-photo-3076787.jpeg?cs=srgb&dl=pexels-bella-zhong-3076787.jpg&fm=jpg",
38 "title": "Beautiful",
39 "product": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
40 "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus arcu felis bibendum ut tristique et egestas. Proin libero nunc consequat interdum. Nulla facilisi etiam dignissim diam quis enim. Cum sociis natoque penatibus et magnis. Quam pellentesque nec nam aliquam. Viverra orci sagittis eu volutpat odio facilisis mauris sit amet. Lorem ipsum dolor sit amet consectetur adipiscing. Bibendum est ultricies integer quis auctor elit.",
41 "price": 80000
42 },
43 {
44 "id": 6,
45 "image": "https://images.pexels.com/photos/6050917/pexels-photo-6050917.jpeg?cs=srgb&dl=pexels-ox-street-6050917.jpg&fm=jpg",
46 "title": "Beautiful",
47 "product": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
48 "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus arcu felis bibendum ut tristique et egestas. Proin libero nunc consequat interdum. Nulla facilisi etiam dignissim diam quis enim. Cum sociis natoque penatibus et magnis. Quam pellentesque nec nam aliquam. Viverra orci sagittis eu volutpat odio facilisis mauris sit amet. Lorem ipsum dolor sit amet consectetur adipiscing. Bibendum est ultricies integer quis auctor elit.",
49 "price": 80000
50 }
51 ]
52}
  • To connect the mock server to your layout page/file;
  • Create a state in the layout.jsx called productData and set it to null.
  • Install Axios with the following command.
1npm install axios
  • To fetch the API/ mock server use the axios. get and pass a response to it.
  • Set the productData to the response and the data it will fetch all this will be done in a react useEffect hook.
1...
2const [productState, setProductState] = useState(null);
3 const [loading, setLoading] = useState(true);
4 useEffect(() => {
5 axios
6 .get("http://localhost:3003/productData")
7 .then((response) => {
8 setProductState(response.data);
9 setLoading(false);
10 });
11 }, []);
12...
  • Create a ternary operator that checks if it is loading, on load it should show the page with the data.
  • Map through the data as seen below.
  • On your terminal run the following command
1npx json-server --watch db.json --port 3003
  • This will start up the mock server and connect it to the front-end.
  • Run the project using npm start, this is how it will look on your browser.

  • For you to be able to upload both image and video you need to create a ternary operator that checks if the item is an image or video.
  • If it an image it should show an image and if is a video it should show a video.
1...
2<Container>
3 <Row xs={1} md={3} className="g-4">
4 {loading && (
5 <Spinner
6 animation="border"
7 variant="dark"
8 style={{ alignItem: "center", margin: "auto" }}
9 />
10 )}
11 {productState === null ? (
12 <div></div>
13 ) : (
14 productState.map((item, index) => (
15 <Col key={`${item.id}${index}`}>
16 <Card>
17 <Link to={`/${item.id}`}>
18 {item.image.includes("image") ? (
19 <Card.Img
20 variant="top"
21 src={item.image}
22 alt="shoe"
23 style={{
24 maxWidth: "415px",
25 maxHeight: "350px",
26 objectFit: "cover",
27 }}
28 />
29 ) : (
30 <video
31 className="videoset"
32 // width="353"
33 // height="350"
34 controls
35 src={item.image}
36 type="video/mp4">
37 Your browser does not support HTML video.
38 </video>
39 // <iframe
40 // width="353"
41 // height="350"
42 // src={item.image}
43 // frameborder="0"
44 // allowfullscreen></iframe>
45 )}
46 </Link>
47
48 <Card.Body>
49 <Card.Body>
50 <Card.Title>{item.title}</Card.Title>
51 <Card.Text>{item.product}</Card.Text>
52 <Card.Text>{item.price}</Card.Text>
53 </Card.Body>
54 </Card.Body>
55 </Card>
56 </Col>
57 ))
58 )}
59 </Row>
60 </Container>

###Create component for the product: This page contains details of each of the items as seen . Before you do this install react-router.

1npm install react-router-dom@6

On your app.js file. Create different routes for all the files/pages

1<BrowserRouter>
2 <Header />
3 <Routes>
4 <Route exact path="/" element={<Layout />}></Route>
5 <Route exact path="/:productId" element={<Product />} />
6 <Route exact path="admin" element={<Admin />} />
7 <Route exact path="superadmin" element={<SuperAdmin />} />
8 {/* <Route index element={<LeagueStandings />} /> */}
9 </Routes>
10 <Footer />
11 </BrowserRouter>

For the product link;

  • Add a product id to the route.
  • This is because you want to choose just an item.
  • Go back to your product page and import useParams, create a function that calls the id you created on the app.js page and assign the useParams to it.
  • useParams enables you to access the parameter of the current route. -Just like the layout component create a state called productState and set it to null. -Use the axios.get method to call the fetch the API. Remember you are fetching a particular data, not all the data. -To do this append an productId to the end of the endpoint you are calling.
1...
2 const [productState, setProductState] = useState(null);
3 const [loading, setLoading] = useState(true);
4 const { productId } = useParams();
5
6 useEffect(() => {
7 axios.get(`http://localhost:3003/productData/${productId}`).then((response) => {
8 setProductState(response.data);
9 setLoading(false);
10 });
11
12 }, [productId]);
13...
  • Create a ternary operator that checks if the state productState is equal to null, shows the loading spinner else the product details.
  • In the container component, set a key called productState.productDetails.
  • Just like you did for the layout component, create a ternary operator that checks if the item is an image or video.
1...
2 <div>
3
4 {productState === null ? (
5 <div>
6 {" "}
7 <Spinner
8 animation="border"
9 variant="dark"
10 style={{ alignItem: "center", margin: "auto" }}
11 />
12 </div>
13 ) : (
14 <Container
15 id="contain"
16 bg="dark"
17 variant="dark"
18 key={productState.productDetails}>
19 <Row>
20 <Col sm={6}>
21 <Card>
22 {productState.image.includes("image") ? (
23 <Card.Img
24 variant="top"
25 src={productState.image}
26 style={{
27 maxWidth: "635px",
28
29 maxHeight: "570px",
30 objectFit: "cover",
31 }}
32 />
33 ) : (
34 <div>
35 <iframe
36 className="videosets"
37 width="543"
38 height="570"
39 src={productState.image}
40 frameborder="0"
41 allowfullscreen></iframe>
42 </div>
43 )}
44 </Card>
45 </Col>
46 <Col sm={6}>
47 <ListGroup style={{ marginBottom: "20px" }}>
48 <ListGroup.Item
49 style={{ fontSize: "25px", fontWeight: "bold"
50 }}>
51 {productState.title}
52 </ListGroup.Item>
53 <ListGroup.Item>{productState.content}
54 </ListGroup.Item>
55 <ListGroup.Item>{productState.price}
56 </ListGroup.Item>
57 </ListGroup>
58 <Button
59 variant="dark"
60 style={{
61 display: "block",
62 margin: "auto",
63 width: "150px",
64 textAlign: "center",
65 }}>
66 Add To Cart
67 </Button>
68 </Col>
69 </Row>
70 </Container>
71...

Bravo! You are almost done with the project. Next, you are to create a component called superAdmin in the components folder. This is where the admin gets to upload either an image or video to cloudinary.

You will be creating many states to hold different data on this component. First, create the file state that helps keep track of the file. Create a function called handleImageUpload. Inside this function, create a function called a file that calls the file event. Create another state called isImageUploading and set it to false. In the handleImageUpload, set it to true; this is the loading state. Next, create a form data function that holds a new form data, append the file to the form data, and append the upload_preset to the form data. The upload_preset is very important when uploading media content to cloudinary. To make it dynamic so admin can input their upload_preset Cloudinary Id, create a state called presetState, and set it as a string. Create a function called handlePresetName and set the preset state to get a value.

Create a post request with the Cloudinary upload API. Set the cloud name dynamically. Like the upload_preset, the cloudname is your cloudinary name, and it is needed for you to make an upload. Below is the code.

1const [file, setFile] = useState("");
2 const [image, setImage] = useState("");
3 const [title, setTitle] = useState("");
4 const [product, setProduct] = useState("");
5 const [content, setContent] = useState("");
6 const [price, setPrice] = useState("");
7 const [isImageUploading, setIsImageUploading] = useState(false);
8 const navigate = useNavigate();
9 const [cldCloudName, setCldCloudName] = useState("");
10 const [presetState, setPresetState] = useState("");
11
12 const handleCloudName = (e) => {
13 setCldCloudName(e.target.value);
14 };
15 const handlePresetName = (e) => {
16 setPresetState(e.target.value);
17 };
18 const handleImageUpload = (e) => {
19 const file = e.target.files[0];
20 setIsImageUploading(true);
21
22 const formData = new FormData();
23 formData.append("file", file);
24 formData.append("upload_preset", presetState);
25
26 fetch(`https://api.cloudinary.com/v1_1/${cldCloudName}/upload`, {
27 method: "POST",
28 body: formData,
29 })
30 .then(handleErrors)
31 .then((response) => response.json())
32 .then((data) => {
33 setIsImageUploading(false);
34 setFile("");
35 setImage(data.secure_url);
36 })
37 .catch((error) => {
38 setIsImageUploading(false);
39 console.log(error);
40 });
41 };

You will create a function called handleFormSubmit, fetch the custom API. Use the post method to send the data to the product page as seen below.

1const handleFormSubmit = (e) => {
2 e.preventDefault();
3 fetch("https://node-cloudcommerce.herokuapp.com/productData", {
4 method: "POST",
5 headers: {
6 "Content-Type": "application/json",
7 },
8 body: JSON.stringify({
9 title,
10 image,
11 product,
12 content,
13 price,
14 cldCloudName,
15 presetState,
16 }),
17 })
18 .then(handleErrors)
19 .then((response) => response.json())
20 .then((data) => {
21 setTitle("");
22 setImage("");
23 setProduct("");
24 setContent("");
25 setPrice("");
26 setCldCloudName("");
27 setPresetState("");
28 navigate("/");
29 })
30 .catch((error) => console.log(error));
31 };

Create the input element for all the data. This is what the product page looks like.

To host the backend, go to Heroku https://devcenter.heroku.com/articles/heroku-cli Follow the steps to host your custom data on Heroku, get your new API from Heroku. Below is the GitHub repo that contains the codebase {% github wise4rmgod/Cloudinray-Ecommercesite %} You can take a look at the codebase on code sandbox: https://codesandbox.io/s/lucid-feynman-v55wp

Conclusion:

You can now test the application by uploading videos and images. You must add the cloud name and upload_preset id first before uploading media content to Cloudinary. Cloudinary helps you in saving up your media content to the cloud. Using it in your eCommerce application can help you keep track of all the media content used in the application. This way, you don't get to lose any media files.

Nwokocha Wisdom Maduabuchi

Senior Technical Writer

A software engineer with considerable experience in native Android, Blockchain Technology, Smart Contracts, Etheruem, Developer Advocacy, UIUX Design, Building a static site using Eleventy, MKDocs, Hugo, Jekyll, and Docsify, DITA, Gitbook, AsciiDoc, Readme, confluence, Technical Content Manager, API Documentation, Documentation Engineer, Community building/management, Firebase.
• Developed apps from the ground up and published them successfully on Google Play
• Built open-source library and an open-source contributor/ maintainer at Gnome, Firebase, Kotlin
• Experience modern architecture, and development practices
• Write clean and maintainable codes