Step-by-Step Guide to Building an Amazon Clone with ReactJS
Table of contents
- What is an Amazon clone?
- Why build with ReactJS?
- Step 1: Plan Your Amazon Clone Project
- Step 2: Set Up Development Environment
- Step 3: Build Backend with Express
- Step 4: Design Database Schema
- Step 5: Build Homepage/Storefront
- Step 6: Build Individual Product Pages
- Step 7: Implement Shopping Cart
- Step 8: Build Checkout Process
- Step 9: Add User Authentication
- Step 10: Add Admin Features
- Conclusion
This article will walk through building a full-featured e-commerce website clone of Amazon using the React JavaScript library. Read along to learn how to set up the development environment, build out the frontend and backend, implement user authentication, and add an admin dashboard.
What is an Amazon clone?
An Amazon clone is a replica e-commerce website that mimics the core functionality and features of the popular online retail giant Amazon.
It allows developers to build a full-fledged online store from scratch by modeling it after an existing successful example. Amazon clones typically include storefronts for browsing products, individual product pages, shopping carts, user accounts, admin dashboards, and more.
Why build with ReactJS?
React is a perfect fit for building an Amazon clone script for several reasons:
Performance - React uses a virtual DOM for rendering UI rather than mutating the real DOM. This makes the app fast and efficient for an e-commerce site with lots of dynamic content.
Components - React's component-based architecture lends itself well to building reusable UI elements like product cards, forms, etc that can be composed to create complex pages.
Declarative Code - React uses declarative JavaScript code that is easier to read and reason about compared to imperative style. This is ideal for large codebases.
Popularity - React is one of the most widely used frontend frameworks currently. Developing the clone in a popular library ensures skills will remain relevant and codebase maintenance is simpler.
Testing - React components are fairly isolated and self-contained, making them easier to test than alternatives with tangled dependencies between components.
Step 1: Plan Your Amazon Clone Project
Before starting development, it's important to plan out the scope and requirements of the project. Here are some things we need to consider:
Features:
Storefront/homepage for browsing categories, searching products
Individual product pages
Shopping cart
Checkout flow
User accounts/login
Admin area
Timeline: We'll break development into 4 sprints over 2 months with weekly milestones.
Hosting: We'll deploy the app to Heroku for ease of deployment and scalability. We'll use MongoDB Atlas for the database.
Database: Our data schema will include collections for Products, Users, Orders etc.
With planning done, we're ready to start setting up our development environment.
Step 2: Set Up Development Environment
First, we'll install Node.js and create a new React app:
npm install -g create-react-app
create-react-app amazon-clone
cd amazon-clone
Next, we'll add Express and other packages:
npm install express mongoose cors dotenv react-router-dom
We initialize our Git repo and make initial commits before moving on to the backend.
Step 3: Build Backend with Express
To build the backend, we first create a folder called backend inside our React project.
We'll initialize a Node.js app and install dependencies:
mkdir backend
cd backend
npm init
npm install express mongoose
In server.js we'll require Express and connect to MongoDB:
const express = require('express');
const mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URL, {useNewUrlParser: true});
const app = express();
app.listen(5000, () => console.log('Backend server running'));
Next we'll create a Product model and route:
// product.model.js
const mongoose = require('mongoose');
const productSchema = new mongoose.Schema({
// schema
});
module.exports = mongoose.model('Product', productSchema);
// routes/product.routes.js
const express = require('express');
const router = express.Router();
const Product = require('../models/product.model');
router.get('/products', async (req, res) => {
const products = await Product.find();
res.json(products);
});
module.exports = router;
Our basic backend is now ready to serve our frontend.
Step 4: Design Database Schema
Next we'll define our database schema in MongoDB. We'll need collections for:
Products
- id, name, image, price, description, category etc.
Users
- id, name, email, password
Orders
- id, user_id, products, total, date
Categories
- name, products
To seed sample data, we import the data and save to the db:
// seeder.js
import Product from './models/Product';
const products = [
{
name: 'Phone',
//...
},
//...
]
const insertData = async () => {
try {
await Product.insertMany(products);
} catch (err) {
console.log(err)
}
}
insertData();
Step 5: Build Homepage/Storefront
Now onto building the frontend! We'll start with the storefront pages.
In App.js we setup routing:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
</Routes>
</BrowserRouter>
)
}
In HomePage.js, we fetch products from the API:
// HomePage.js
import { useEffect, useState } from 'react';
import axios from 'axios';
function HomePage() {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
const response = await axios.get('/api/products');
setProducts(response.data);
}
fetchProducts();
}, []);
return (
<div>
{products.map(product => (
<ProductCard product={product} key={product._id} />
))}
</div>
)
}
We'll display Products in a reusable ProductCard component. This establishes the basic storefront UI.
Step 6: Build Individual Product Pages
To view a product, we create a ProductDetailsPage:
// ProductDetailsPage.js
const { useState, useEffect } = require('react');
const { useParams } = require('react-router-dom');
function ProductDetailsPage() {
const [product, setProduct] = useState({});
const { id } = useParams();
useEffect(() => {
const fetchProduct = async () => {
const response = await axios.get(`/api/products/${id}`);
setProduct(response.data);
}
fetchProduct();
}, [id]);
return (
<div>
<h1>{product.name}</h1>
<img src={product.image} />
<p>{product.description}</p>
<button>Add to Cart</button>
</div>
)
}
Now users can view details of individual products.
Step 7: Implement Shopping Cart
For the cart, we'll store items in the browser's localStorage:
// useCart.js
const getCartFromStorage = () => {
let cart;
if (typeof window !== 'undefined') {
cart = localStorage.getItem('cart')
? JSON.parse(localStorage.getItem('cart'))
: [];
}
return cart;
}
const useCart = () => {
const [cart, setCart] = useState(getCartFromStorage());
const addToCart = (product) => {
setCart([...cart, {...product, quantity: 1}]);
}
useEffect(() => {
localStorage.setItem('cart', JSON.stringify(cart));
}, [cart])
return { cart, addToCart }
}
export default useCart;
Now any component can import useCart() to access the cart state. We'll call addToCart() on the product details page.
Step 8: Build Checkout Process
For checkout, we collect customer details over multiple pages:
// CheckoutPage1.js
function CheckoutPage1() {
const [name, setName] = useState('');
const handleSubmit = () => {
// submit to next page
}
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button type="submit">Next</button>
</form>
)
}
// CheckoutPage2.js
// collect address
// submit to payment page
// CheckoutPage3.js
// payment form
// submit order
This splits up the complex checkout into simple, sequential steps.
Step 9: Add User Authentication
To save orders against user profiles, we will implement user authentication.
First, we create a User model:
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
orders: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Order'
}]
})
module.exports = mongoose.model('User', userSchema);
We'll sign users up and log them in via JSON web tokens (JWT):
// auth.js
const jwt = require('jsonwebtoken');
const register = async (req, res) => {
// create user
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
res.json({ token });
}
const login = async (req, res) => {
// authenticate
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
res.json({ token });
}
On the frontend, we use the token for authorization:
// AuthContext.js
const login = async ( credentials ) => {
const { token } = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
}).then(res => res.json());
localStorage.setItem('token', token);
}
Finally, we check for an auth token on protected routes:
// ProtectedRoute.js
function ProtectedRoute({ children }) {
const token = localStorage.getItem('token');
if(!token) return <Navigate to="/login" />
return children
}
This implements user authentication for the app.
Step 10: Add Admin Features
For admin features, we'll build a protected admin route and dashboard:
// App.js
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/admin"
element={
<RequireAuth>
<AdminDashboard />
</RequireAuth>
}
/>
</Routes>
// RequireAuth.js
function RequireAuth({ children }) {
const { user } = useAuth();
if(!user) {
return <Navigate to="/login" />
}
return children;
}
The dashboard will display analytics and allow CRUD operations:
// AdminDashboard.js
return (
<div>
<h1>Admin Dashboard</h1>
<AnalyticsSection />
<Route path="products">
<ProductsTable />
<NewProductForm />
</Route>
<Route path="orders">
<OrdersTable />
</Route>
</div>
)
We can retrieve and update data by building API methods:
// products.api.js
export const getProducts = async () => {
const res = await axios.get('/api/admin/products');
return res.data;
}
export const deleteProduct = async (id) => {
await axios.delete(`/api/admin/products/${id}`);
}
Conclusion
In this tutorial, we built a fully functional Amazon clone app from end-to-end with React, Express, and MongoDB. Key takeaways included:
Planning the scope and architecture of the project
Setting up a production-ready development environment
Building reusable UI components
Creating RESTful APIs with Express
Implementing user authentication
Storing app data in MongoDB
Securing admin-only routes and functionality
Some enhancements could include payment integrations, additional product filters, wishlists, and integration with email/SMS libraries. With further work, this clone application could become a full-fledged e-commerce site.
Subscribe to my newsletter
Read articles from prasad venkatachalam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
prasad venkatachalam
prasad venkatachalam
With over 10 years in web and app development, I'm more interested in writing about clone solutions of popular brands