Build a shopping cart in Node.js and React

Sunil Joshi
Sep 7 · 7 min read
Image for post
Image for post

In this article we are going to build shopping cart frontend with React for our application. This will be simple tutorial on react eCommerce.

You can check our backend part built in Node.js, which we already have published.

As far as we can, this will be minimal for full understanding of the main functionality.

To begin, we need to setup our React application using create-react-app.

npx create-react-app shopping-cart
cd shopping-cart
code .
npm start

You might need to install the react CLI first on your local machine if you haven’t before.

The code . command opens the project in visual studio code.

We can now discard the things we don’t need in App.js, and also get rid of the files (App.css and index.css).

To the main work

We will continue by setting up our user interface for the application. You can get all our UI components from WrapPixel’s UI Kit.

WrapPixel is an online template store where you could get great free react dashboard and react bootstrap templates.

We will add the bootstrap CDN into our root index.html file inside the public directory.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Shopping cart</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

Then we add a Navbar.js file to the components folder. This is where we will handle our routing.

import React from "react"
import {
Link
} from "react-router-dom"
export const Navbar = () => {
return ( <
nav className = "navbar navbar-expand-lg navbar-light bg-info" >
<
div className = "container" >
<
Link to = "/"
className = "navbar-brand" > Vue Cart < /Link>
<
div className = "collapse navbar-collapse justify-content-end"
id = "navbarNav" >
<
ul className = "navbar-nav" >
<
li className = "nav-item active" >
<
Link to = "/"
className = "nav-link" > Home < /Link> < /
li > <
li className = "nav-item" >
<
Link to = "/cart"
className = "nav-link" > Cart < /Link> < /
li > <
/ul> < /
div > < /div> < /
nav >
)
}

Remember that we are using react-router-dom to route pages, so we need to add Navbar below our switch, as seen below ‘App.js’

import React from "react"
import { Switch } from "react-router-dom"
import { Navbar } from "./components/Navbar"
import "./App.css"
function App() {
return (
<div className='App'>
<Navbar />
<Switch>
// Our pages will go here
</Switch>
</div>
)
}
export default App

Secondly, we create a page folder. This folder houses our pages(product and cart page), all our service and views will be rendered in the pages for routing.

Let’s create a simple Product.js

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
export const Products = () => {
const [products, setProducts] = useState([]);
const [hasError, setError] = useState(false);
async function fetchData() {
const res = await fetch("http://localhost:4000/product");
res
.json()
.then((res) => {
console.log(res.data);
setProducts(res.data);
})
.catch((error) => {
setError(error);
});
}
async function addToCart(id, quantity) {
try {
const response = await fetch("http://localhost:4000/cart", {
method: "POST",
body: JSON.stringify({
productId: id,
quantity: quantity,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
});
let data = await response.json();
alert("Item Added To Cart");
console.log(data);
} catch (err) {
alert("Something Went Wrong");
console.log(err);
}
}
useEffect(() => {
fetchData();
}, []);
console.log(products);
return (
<main>
<section>
<div className="banner-innerpage">
<div className="container">
<div className="row justify-content-center">
<div className="col-md-6 align-self-center text-center">
<h1 className="title">Shop listing</h1>
<h6 className="subtitle op-8">
We are small team of creative people working together
</h6>
</div>
</div>
</div>
</div>
</section>
<section>
<div className="spacer">
<div className="container">
<div className="row mt-5">
<div className="col-lg-9">
<div className="row shop-listing">
{products.map((product, i) => (
<div className="col-lg-4">
<div className="card shop-hover border-0">
<img
src={"http://localhost:4000/" + product.image}
alt="wrapkit"
className="img-fluid"
/>
<div className="card-img-overlay align-items-center">
<button
onClick={(e) => addToCart(product._id, 1)}
className="btn btn-md btn-info"
>
Add to cart
</button>
</div>
</div>
<div className="card border-0">
<h6>
<a href="#" className="link">
{product.name}{" "}
</a>
</h6>
<h6 className="subtitle">by Wisdom</h6>
<h5 className="font-medium m-b-30">
$195 /{" "}
<del className="text-muted line-through">$225</del>
</h5>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</section>
</main>
);
};

Noticed the fetchData function? We make a http request to the backend to list all products and store in the variable products (we are using React hooks, remember).

Since we have it as an array now, we loop through it to display as seen on line 64.

We will also need to add items to cart, which will be an async method making a request to the backend with its parameters passed to it. This is a very important feature as well.

The addToCart is defined on line 18:

async function addToCart(id, quantity) {
try {
const response = await fetch("http://localhost:4000/cart", {
method: "POST",
body: JSON.stringify({
productId: id,
quantity: quantity,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
let data = await response.json()
console.log(data)
} catch (err) {
console.log(err)
}
}

After that, We add the event listener to the button to call the addToCart button

<button
onClick={(e) => addToCart(product._id, 1)}
className="btn btn-md btn-info"
>
Add to cart
</button>

Here, we pass the id of the product and a default quantity as 1.

Then add to our App.js once again, as a page.

import React from "react"
import { Switch, Route } from "react-router-dom"
import { Products } from "./pages/product"
import { Navbar } from "./components/Navbar"import "./App.css"
function App() {
return (
<div className='App'>
<Navbar />
<Switch>
<Route exact path='/' component={Products} />
</Switch>
</div>
)
}
export default App
Image for post
Image for post

Let’s head over to cart page and add our simple UI.

We then add a method to fetch a list of our cart items from the backend. Notice the fetchCart method below. Doing as pleased.

If you are not familiar with react hooks, you could always look it up or better still use the component based.

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import "./cart.css";
export const Cart = (props) => {
const [carts, setCarts] = useState([]);
const [payload, setPayloader] = useState({});
const [hasError, setError] = useState(false);
async function fetchCart() {
const res = await fetch("http://localhost:4000/cart");
res
.json()
.then((res) => {
console.log(res.data.items);
setCarts(res.data.items);
setPayloader(res.data);
})
.catch((error) => {
setError(error);
});
}
async function increaseQty(id) {
try {
const res = await fetch("http://localhost:4000/cart", {
method: "POST",
body: JSON.stringify({
productId: id,
quantity: 1,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
});
console.log(res);
fetchCart();
alert("Item Increamented");
} catch (err) {
console.log(err);
}
}
async function emptyCart() {
try {
const res = await fetch("http://localhost:4000/cart/empty-cart", {
method: "DELETE",
});
await res.json();
fetchCart();
props.history.push("/");
} catch (err) {
console.log(err);
}
}
useEffect(() => {
fetchCart();
}, []);
return (
<main>
<section>
<div className="banner-innerpage">
<div className="container">
<div className="row justify-content-center">
<div className="col-md-6 align-self-center text-center">
<h1 className="title">Cart Listing</h1>
<h6 className="subtitle op-8">
We are small team of creative people working together
</h6>
</div>
</div>
</div>
</div>
</section>
<section>
<div className="spacer">
<div className="container">
<div className="row mt-5">
<div className="col-lg-9">
<div className="row shop-listing">
<table className="table shop-table">
<tr>
<th className="b-0">Name</th>
<th className="b-0">Price</th>
<th className="b-0">Quantity</th>
<th className="b-0 text-right">Total Price</th>
</tr>
{carts.map((item, i) => (
<tr>
<td>{item.productId.name}</td>
<td>{item.productId.price}</td>
<td>
<button
onClick={(e) => increaseQty(item.productId._id)}
className="btn btn-primary btn-sm"
>
+
</button>
{item.quantity}
<button className="btn btn-primary btn-sm">-</button>
</td>
<td className="text-right">
<h5 className="font-medium m-b-30">{item.total}</h5>
</td>
</tr>
))}
<tr>
<td colspan="3" align="right">
Subtotal :{payload.subTotal}
</td>
<td colspan="4" align="right">
<button
className="btn btn-danger"
onClick={(e) => emptyCart()}
>
Empty cart
</button>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
);
};

We can then loop through the array(cart) and modify.

Notice the increamentQty(id) method which takes the product id as a parameter and then set the quantity to one as default as we are updating the quantity by one.

async function increaseQty(id) {
try {
const res = await fetch("http://localhost:4000/cart", {
method: "POST",
body: JSON.stringify({
productId: id,
quantity: 1,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
});
console.log(res);
fetchCart();
alert("Item increamented");
} catch (err) {
console.log(err);
}
}

After doing that we added click event to our button to trigger the method:

<button
onClick={(e) => increaseQty(item.productId._id)}
className="btn btn-primary btn-sm"
>
+
</button>

Clicking on the button will increment the quantity of the item.

We then define an emptyCart method as well to delete all items currently in the cart. See below;

async function emptyCart() {
try {
const res = await fetch("http://localhost:4000/cart/empty-cart", {
method: "DELETE",
});
await res.json();
fetchCart();
props.history.push("/");
} catch (err) {
console.log(err);
}
}
Image for post
Image for post

Exercise

  • Implement remove product from cart

After implementing this, Push your work to git and add the link in the comment section. Lets have some fun😁

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

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