๐๏ธ How I Built a Production-Ready E-Commerce Platform with React (And You Can Too!)
TL;DR: I built a complete e-commerce frontend called ShopStyle using React 18, Tailwind CSS, and modern patterns. It features real-time search, a persistent cart, dark mode, and 60fps animations. This article breaks down the architecture, challenges, and key decisions.
Live Demo โข GitHub
๐ฏ The Challenge
"How hard could it be?" I asked myself before building an e-commerce platform from scratch. The answer: delightfully challenging.
My goals were to:
- Create a realistic shopping experience with real product data
- Implement modern React patterns (not just another Todo app)
- Focus on performance (95+ Lighthouse score target)
- Make it beautiful with smooth animations and responsive design
- Keep the code maintainable with clean architecture
Hereโs what I built and how I did it.
๐ The Result: ShopStyle
๐ Live Demo โข ๐ป Source Code
Key Features
- โ Real-time search with DummyJSON API
- โ Persistent shopping cart (localStorage + cross-tab sync)
- โ Dark / light mode with system preference detection
- โ 60fps animations using Framer Motion
- โ Fully responsive, mobile-first design
- โ Performance optimized (95+ Lighthouse score)
๐๏ธ Architecture Overview
graph TB
A[App.jsx] --> B[ThemeContext]
A --> C[ProductContext]
A --> D[CartContext]
C --> E[ProductList]
E --> F[ProductCard]
D --> G[CartSidebar]
D --> F
H[SearchBar] --> I((DummyJSON API))
I --> C
B --> A
`
Each context handles a single responsibility, keeping components focused, reusable, and easy to reason about.
โ๏ธ Tech Stack Decisions
| Technology | Why I Chose It | Alternative |
|---|---|---|
| React 18 | Stable ecosystem, concurrent features | Vue, Svelte |
| Vite | Lightning-fast DX | CRA |
| Tailwind CSS | Speed + consistency | Styled Components |
| Context API | Perfect for mid-size apps | Redux, Zustand |
| Framer Motion | Declarative animations | React Spring |
| DummyJSON | Realistic fake data | MSW |
๐ก Implementation Deep Dives
๐ Real-Time Search with Debounce
`
import { useState, useEffect } from "react";
const SearchBar = ({ onSearch }) => {
const [query, setQuery] = useState("");
const [debouncedQuery, setDebouncedQuery] = useState("");
useEffect(() => {
const timer = setTimeout(() => setDebouncedQuery(query), 300);
return () => clearTimeout(timer);
}, [query]);
useEffect(() => {
if (!debouncedQuery.trim()) {
onSearch(null);
return;
}
const search = async () => {
try {
const res = await fetch(
`https://dummyjson.com/products/search?q=${debouncedQuery}`
);
const data = await res.json();
onSearch(data.products);
} catch {
onSearch([]);
}
};
search();
}, [debouncedQuery, onSearch]);
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search products..."
className="w-full px-4 py-2 border rounded-lg"
/>
);
};
export default SearchBar;
๐ Persistent Cart with Cross-Tab Sync
import { useState, useEffect } from "react";
const [cart, setCart] = useState(() => {
const stored = localStorage.getItem("shopstyle-cart");
return stored ? JSON.parse(stored) : [];
});
useEffect(() => {
localStorage.setItem("shopstyle-cart", JSON.stringify(cart));
}, [cart]);
useEffect(() => {
const sync = (e) => {
if (e.key === "shopstyle-cart") {
setCart(JSON.parse(e.newValue || "[]"));
}
};
window.addEventListener("storage", sync);
return () => window.removeEventListener("storage", sync);
}, []);
๐ Theme System (System-Aware)
import { useState, useEffect, useCallback } from "react";
const [theme, setTheme] = useState(() => {
const saved = localStorage.getItem("shopstyle-theme");
if (saved) return saved;
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
});
useEffect(() => {
document.documentElement.className = theme;
localStorage.setItem("shopstyle-theme", theme);
}, [theme]);
const toggleTheme = useCallback(() => {
setTheme((prev) => (prev === "light" ? "dark" : "light"));
}, []);
โก Performance Optimizations
Memoized Product Cards
import { memo } from "react";
const ProductCard = memo(({ product }) => {
return <div>{product.title}</div>;
});
export default ProductCard;
Virtualized Lists for Large Catalogs
import { FixedSizeList as List } from "react-window";
const VirtualProductList = ({ products }) => (
<List height={600} itemCount={products.length} itemSize={350} width="100%">
{({ index, style }) => (
<div style={style}>
<ProductCard product={products[index]} />
</div>
)}
</List>
);
๐งช Lighthouse Scores
| Metric | Score |
|---|---|
| Performance | 96 |
| Accessibility | 100 |
| Best Practices | 100 |
| SEO | 92 |
๐ Deployment
vercel --prod
Vercel handled builds, previews, routing, and performance out of the box.
๐ค Lessons Learned
- Cart sync across tabs requires
storageevents - Animations should stick to
transformandopacity - Always plan for API failure and caching
๐ฎ Whatโs Next
- Backend (Node + PostgreSQL)
- Stripe payments
- PWA support
- Internationalization
๐ ๏ธ Run It Locally
git clone https://github.com/yourusername/shopstyle.git
cd shopstyle
npm install
npm run dev
๐ฌ Letโs Talk
- What would you improve?
- How do you manage state in large React apps?
- Want a backend follow-up?
Drop a comment ๐
๐ Thanks
Huge thanks to React, Tailwind, DummyJSON, and the DEV community โค๏ธ
โญ Star the repo
๐ Share with friends
๐ฆ Follow for more content
Happy coding โ the best project is the one you actually finish. ๐
Top comments (0)