Learn how to build a functional shopping cart using Replit. Follow this step-by-step guide to create, test, and deploy your online store easily.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
To build a simple yet functional shopping cart in Replit, create a small full-stack app using Node.js, Express, and a bit of frontend HTML/JavaScript. Replit automatically sets up Node for you if you pick the “Node.js” template. You’ll have a backend server (for handling cart and items) and frontend files (for displaying products and interacting with the cart). Start from your main index.js (or server.js) file, create a public folder for frontend files, and use Replit’s built-in web server preview to test it live. This approach works reliably in Replit — no complex setup, and it uses built-in Express serving and session logic properly.
When you create a new Replit using the "Node.js" template, Replit gives you:
Create a folder named public manually (using the Replit file explorer) and inside it add these files:
npm install express body-parser
These two are enough for a simple cart backend. Express builds your web server, and body-parser helps to read JSON sent from frontend.
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware setup
app.use(bodyParser.json());
app.use(express.static("public")); // serve files from the "public" folder
// Pretend DB (in-memory for demo)
let products = [
{ id: 1, name: "T-Shirt", price: 20 },
{ id: 2, name: "Shoes", price: 50 },
{ id: 3, name: "Hat", price: 15 }
];
let cart = []; // will store added product IDs
// Route to get all products
app.get("/api/products", (req, res) => {
res.json(products);
});
// Route to get cart contents
app.get("/api/cart", (req, res) => {
res.json(cart);
});
// Route to add an item to the cart
app.post("/api/cart", (req, res) => {
const { id } = req.body;
const product = products.find(p => p.id === id);
if (product) {
cart.push(product);
res.json({ message: "Item added", cart });
} else {
res.status(400).json({ error: "Product not found" });
}
});
// Route to clear the cart
app.post("/api/cart/clear", (req, res) => {
cart = [];
res.json({ message: "Cart cleared" });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Place this entire code in your existing index.js. Replit automatically runs this file. The app.use(express.static("public")) line serves your frontend automatically.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Replit Shopping Cart</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>My Shop</h1>
<div id="product-list"></div>
<h2>Cart</h2>
<div id="cart"></div>
<button id="clear-cart">Clear Cart</button>
<script src="script.js"></script>
</body>
</html>
This file shows the products, the cart, and a button to clear it. It loads script.js which we’ll create next.
// Fetch and display products
async function loadProducts() {
const res = await fetch("/api/products");
const data = await res.json();
const listDiv = document.getElementById("product-list");
listDiv.innerHTML = "";
data.forEach(p => {
const div = document.createElement("div");
div.innerHTML = `${p.name} - $${p.price} <button>Add to cart</button>`;
div.querySelector("button").onclick = () => addToCart(p.id);
listDiv.appendChild(div);
});
}
// Fetch and display cart
async function loadCart() {
const res = await fetch("/api/cart");
const items = await res.json();
const cartDiv = document.getElementById("cart");
cartDiv.innerHTML = items.map(i => `${i.name} - $${i.price}`).join("<br>");
}
// Add product to cart
async function addToCart(id) {
await fetch("/api/cart", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id })
});
loadCart();
}
// Clear cart
document.getElementById("clear-cart").onclick = async () => {
await fetch("/api/cart/clear", { method: "POST" });
loadCart();
};
loadProducts();
loadCart();
All this code goes in public/script.js. It talks to your Express routes using fetch() requests. Replit previews it automatically when you click “Run”.
Click Replit’s green Run button. It will start your Express server and open the web view on the right side. You should see your shop, be able to add products, and watch them appear in the cart. The Run button in Replit uses the “run” command defined in package.json — keep it as "start": "node index.js" or let Replit manage it automatically.
process.env.MY\_KEY)
This setup gives you a working, understandable shopping cart directly in Replit — no external tools needed, just clear file structure, valid Express routes, and live reload experience that’s typical of a full Replit-based Node project.
import express from "express";
import bodyParser from "body-parser";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(bodyParser.json());
app.post("/cart/:userId/add", async (req, res) => {
const { userId } = req.params;
const { productId, quantity } = req.body;
const userCart = (await db.get(`cart_${userId}`)) || {};
userCart[productId] = (userCart[productId] || 0) + quantity;
await db.set(`cart_${userId}`, userCart);
res.json({ message: "Item added", cart: userCart });
});
app.get("/cart/:userId", async (req, res) => {
const { userId } = req.params;
const userCart = (await db.get(`cart_${userId}`)) || {};
res.json(userCart);
});
app.post("/cart/:userId/remove", async (req, res) => {
const { userId } = req.params;
const { productId } = req.body;
const userCart = (await db.get(`cart_${userId}`)) || {};
delete userCart[productId];
await db.set(`cart_${userId}`, userCart);
res.json({ message: "Item removed", cart: userCart });
});
app.listen(3000, () => console.log("Server running on port 3000"));
import express from "express";
import bodyParser from "body-parser";
import fetch from "node-fetch";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(bodyParser.json());
async function createPaymentIntent(amount) {
const response = await fetch("https://api.stripe.com/v1/payment\_intents", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.STRIPE_SECRET_KEY}`,
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({
amount: Math.round(amount \* 100),
currency: "usd"
})
});
return response.json();
}
app.post("/checkout/:userId", async (req, res) => {
const { userId } = req.params;
const userCart = (await db.get(`cart_${userId}`)) || {};
const productPrices = {
"prod\_apple": 1.5,
"prod\_orange": 2.0,
"prod\_milk": 3.0
};
let total = 0;
for (const [id, qty] of Object.entries(userCart)) {
total += (productPrices[id] || 0) \* qty;
}
if (total === 0) {
return res.status(400).json({ error: "Cart is empty" });
}
try {
const paymentIntent = await createPaymentIntent(total);
res.json({
clientSecret: paymentIntent.client\_secret,
total
});
} catch (err) {
console.error("Payment creation failed:", err);
res.status(500).json({ error: "Payment failed" });
}
});
app.listen(3001, () => console.log("Checkout service running on port 3001"));
import express from "express";
import bodyParser from "body-parser";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(bodyParser.json());
app.post("/cart/:userId/sync", async (req, res) => {
const { userId } = req.params;
const { clientCart, lastUpdated } = req.body;
const serverData = (await db.get(`cart_${userId}`)) || { items: {}, lastUpdated: 0 };
const isClientFresher = lastUpdated > serverData.lastUpdated;
const mergedCart = isClientFresher ? clientCart : serverData.items;
await db.set(`cart_${userId}`, { items: mergedCart, lastUpdated: Date.now() });
res.json({ syncedCart: mergedCart });
});
app.listen(3002, () => console.log("Cart sync service running on port 3002"));

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
A good shopping cart built on Replit should separate the frontend (React or plain HTML + JS) and the backend (Node.js + Express), store secrets safely, and persist data either with a hosted database (like PostgreSQL or MongoDB Atlas) or with Replit’s built-in Replit Database for simple use cases. Always remember that Replit containers sleep, so don’t rely on in-memory variables for persistence. Use environment variables for API keys, and set them in the “Secrets” tab (padlock icon). The overall flow: your frontend sends requests to backend routes like /api/cart to add or fetch items, and the backend handles logic and storage.
In your Replit workspace, you can keep a simple folder layout:
You can serve your frontend directly from Express for simplicity.
Create a new file called server.js in the main directory if it doesn’t exist. Add this boilerplate code:
// server.js
import express from "express"
import bodyParser from "body-parser"
import Database from "@replit/database"
const app = express()
const db = new Database() // Replit's built-in key-value DB
app.use(bodyParser.json())
app.use(express.static("public")) // serve frontend files
// Add item to shopping cart
app.post("/api/cart", async (req, res) => {
const { userId, productId, quantity } = req.body
// Get current cart
let cart = (await db.get(`cart_${userId}`)) || []
const existing = cart.find(item => item.productId === productId)
if (existing) {
existing.quantity += quantity
} else {
cart.push({ productId, quantity })
}
await db.set(`cart_${userId}`, cart)
res.json({ success: true, cart })
})
// Get user's shopping cart
app.get("/api/cart/:userId", async (req, res) => {
const userId = req.params.userId
const cart = (await db.get(`cart_${userId}`)) || []
res.json(cart)
})
app.listen(3000, () => {
console.log("Server running on http://localhost:3000")
})
Where this goes: Put this full code inside server.js. Every route (/api/cart) is called by your frontend.
Now create a folder named public and inside it create index.html. This will be your user interface.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Shopping Cart</title>
</head>
<body>
<h1>Shopping Cart Example</h1>
<div>
<input id="product-id" placeholder="Product ID" />
<input id="quantity" type="number" value="1" />
<button id="add-btn">Add to Cart</button>
</div>
<div>
<button id="view-btn">View Cart</button>
</div>
<ul id="cart-list"></ul>
<script>
const userId = "user1" // simple static user for demo
document.getElementById("add-btn").onclick = async () => {
const productId = document.getElementById("product-id").value
const quantity = parseInt(document.getElementById("quantity").value)
await fetch("/api/cart", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId, productId, quantity })
})
alert("Item added!")
}
document.getElementById("view-btn").onclick = async () => {
const res = await fetch(`/api/cart/${userId}`)
const cart = await res.json()
const list = document.getElementById("cart-list")
list.innerHTML = ""
cart.forEach(item => {
const li = document.createElement("li")
li.textContent = `${item.productId} x ${item.quantity}`
list.appendChild(li)
})
}
</script>
</body>
</html>
This file goes inside the public/ folder. Express automatically serves it from the root path (/).
In the Replit Shell (bottom panel), install dependencies:
npm init -y
npm install express body-parser @replit/database
Then in the Replit “Run” button configuration (top of the editor), edit the .replit file if needed to look like this:
run = "node server.js"
Now click “Run”. It should show “Server running on http://localhost:3000” in the console, and the Replit webview will open showing your simple shopping cart demo.
process.env.MY\_SECRET.Never hardcode secrets directly in your repository.
With this setup, you can safely build and test a working cart system inside Replit, understand real backend/frontend communication, and later extend it with authentication, a real database, or payment integration.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.