E-commerce Store

A modern, full-featured e-commerce storefront with product grid, shopping cart, checkout flow, and customer account pages. Built for conversion.

Product GridShopping CartCheckout FlowUser AccountsOrder History

Overview

Built for Modern Commerce

This template provides everything you need to launch a professional online store. From product discovery to checkout, every interaction is designed for conversion and customer satisfaction.

🛒

Smart Shopping Cart

Persistent cart with real-time updates and quantity management

💳

Streamlined Checkout

Multi-step checkout with progress indicators and form validation

👤

Customer Accounts

User profiles with order history and saved addresses

📱

Mobile-First Design

Responsive layouts optimized for all screen sizes

Live Demo

ShopCo
NEW COLLECTION

Summer Essentials

Discover our curated collection of premium products designed for the modern lifestyle.

Featured Products

🎧
4.8

Premium Headphones

$299

🎒
4.6

Leather Backpack

$189

4.9

Smart Watch

$399

👟
4.7

Running Shoes

$149

🔊
4.5

Wireless Speaker

$129

🕶️
4.4

Sunglasses

$199

Implementation Guide

1

Set Up Project Structure

Create the necessary pages and components for your e-commerce store.

step-1.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Recommended folder structure src/ app/ page.tsx # Home/Store page products/ page.tsx # Product listing [id]/page.tsx # Product detail cart/page.tsx # Shopping cart checkout/page.tsx # Checkout flow account/ page.tsx # Account dashboard orders/page.tsx components/ ProductCard.tsx CartDrawer.tsx CheckoutForm.tsx lib/ cart-context.tsx # Cart state management
2

Create Cart Context

Set up global state management for the shopping cart.

step-2.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"use client"; import { createContext, useContext, useState, ReactNode } from "react"; interface CartItem { id: string; name: string; price: number; quantity: number; image: string; } interface CartContextType { items: CartItem[]; addItem: (item: CartItem) => void; removeItem: (id: string) => void; updateQuantity: (id: string, quantity: number) => void; total: number; } const CartContext = createContext<CartContextType | undefined>(undefined); export function CartProvider({ children }: { children: ReactNode }) { const [items, setItems] = useState<CartItem[]>([]); const addItem = (item: CartItem) => { setItems(prev => { const existing = prev.find(i => i.id === item.id); if (existing) { return prev.map(i => i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i ); } return [...prev, { ...item, quantity: 1 }]; }); }; const removeItem = (id: string) => { setItems(prev => prev.filter(i => i.id !== id)); }; const updateQuantity = (id: string, quantity: number) => { if (quantity <= 0) return removeItem(id); setItems(prev => prev.map(i => i.id === id ? { ...i, quantity } : i) ); }; const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0 ); return ( <CartContext.Provider value={{ items, addItem, removeItem, updateQuantity, total }}> {children} </CartContext.Provider> ); } export const useCart = () => { const context = useContext(CartContext); if (!context) throw new Error("useCart must be used within CartProvider"); return context; };
3

Build Product Grid

Create a responsive product grid with filtering and sorting.

step-3.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
interface Product { id: string; name: string; price: number; image: string; category: string; rating: number; } function ProductGrid({ products }: { products: Product[] }) { return ( <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> {products.map((product) => ( <Link key={product.id} href={`/products/${product.id}`}> <div className="group"> <div className="aspect-square rounded-2xl bg-slate-100 dark:bg-slate-800 overflow-hidden mb-4"> <img src={product.image} alt={product.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" /> </div> <h3 className="font-medium text-slate-900 dark:text-white"> {product.name} </h3> <p className="text-slate-600 dark:text-slate-400"> ${product.price.toFixed(2)} </p> </div> </Link> ))} </div> ); }
4

Implement Checkout Flow

Build a multi-step checkout with shipping and payment forms.

step-4.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function CheckoutForm() { const [step, setStep] = useState(1); const { items, total } = useCart(); return ( <div className="max-w-2xl mx-auto"> {/* Progress indicator */} <div className="flex items-center gap-4 mb-8"> {["Shipping", "Payment", "Review"].map((label, i) => ( <div key={label} className="flex items-center gap-2"> <div className={`w-8 h-8 rounded-full flex items-center justify-center ${ step > i + 1 ? "bg-emerald-500 text-white" : step === i + 1 ? "bg-cyan-600 text-white" : "bg-slate-200 text-slate-500" }`}> {step > i + 1 ? "✓" : i + 1} </div> <span className={`text-sm ${step === i + 1 ? "font-medium" : ""}`}> {label} </span> </div> ))} </div> {/* Step content */} {step === 1 && <ShippingForm onNext={() => setStep(2)} />} {step === 2 && <PaymentForm onNext={() => setStep(3)} onBack={() => setStep(1)} />} {step === 3 && <ReviewOrder items={items} total={total} />} </div> ); }

Full Source Code

ecommerce-store.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"use client"; import { useState, createContext, useContext, ReactNode } from "react"; import Link from "next/link"; // Cart Context interface CartItem { id: string; name: string; price: number; quantity: number; image: string; } interface CartContextType { items: CartItem[]; addItem: (item: Omit<CartItem, "quantity">) => void; removeItem: (id: string) => void; updateQuantity: (id: string, quantity: number) => void; total: number; count: number; } const CartContext = createContext<CartContextType | undefined>(undefined); export function CartProvider({ children }: { children: ReactNode }) { const [items, setItems] = useState<CartItem[]>([]); const addItem = (item: Omit<CartItem, "quantity">) => { setItems(prev => { const existing = prev.find(i => i.id === item.id); if (existing) { return prev.map(i => i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i); } return [...prev, { ...item, quantity: 1 }]; }); }; const removeItem = (id: string) => setItems(prev => prev.filter(i => i.id !== id)); const updateQuantity = (id: string, quantity: number) => { if (quantity <= 0) return removeItem(id); setItems(prev => prev.map(i => i.id === id ? { ...i, quantity } : i)); }; const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0); const count = items.reduce((sum, item) => sum + item.quantity, 0); return ( <CartContext.Provider value={{ items, addItem, removeItem, updateQuantity, total, count }}> {children} </CartContext.Provider> ); } export const useCart = () => { const context = useContext(CartContext); if (!context) throw new Error("useCart must be used within CartProvider"); return context; }; // Product Grid Component interface Product { id: string; name: string; price: number; image: string; category: string; rating: number; } function ProductGrid({ products }: { products: Product[] }) { const { addItem } = useCart(); return ( <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> {products.map((product) => ( <div key={product.id} className="group"> <div className="aspect-square rounded-2xl bg-slate-100 dark:bg-slate-800 overflow-hidden mb-4 relative"> <img src={product.image} alt={product.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" /> <button onClick={() => addItem(product)} className="absolute bottom-4 left-1/2 -translate-x-1/2 px-4 py-2 bg-cyan-600 text-white text-sm font-medium rounded-lg opacity-0 group-hover:opacity-100 transition-opacity" > Add to Cart </button> </div> <div className="flex items-center gap-1 mb-1"> <svg className="w-4 h-4 text-amber-400 fill-current" viewBox="0 0 20 20"> <path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" /> </svg> <span className="text-sm text-slate-600 dark:text-slate-400">{product.rating}</span> </div> <h3 className="font-medium text-slate-900 dark:text-white">{product.name}</h3> <p className="text-slate-600 dark:text-slate-400">${product.price}</p> </div> ))} </div> ); } // Header Component function StoreHeader() { const { count } = useCart(); const [cartOpen, setCartOpen] = useState(false); return ( <header className="sticky top-0 z-50 bg-white/80 dark:bg-slate-950/80 backdrop-blur-xl border-b border-slate-200 dark:border-slate-800"> <div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between"> <div className="flex items-center gap-8"> <Link href="/" className="text-xl font-bold text-slate-900 dark:text-white"> ShopCo </Link> <nav className="hidden md:flex items-center gap-6"> {["New", "Women", "Men", "Accessories", "Sale"].map((item) => ( <Link key={item} href={`/${item.toLowerCase()}`} className={`text-sm font-medium ${ item === "Sale" ? "text-red-500" : "text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white" }`} > {item} </Link> ))} </nav> </div> <div className="flex items-center gap-4"> <button className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg"> <SearchIcon /> </button> <button className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg"> <UserIcon /> </button> <button onClick={() => setCartOpen(true)} className="relative p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg" > <CartIcon /> {count > 0 && ( <span className="absolute -top-1 -right-1 w-5 h-5 bg-cyan-600 text-white text-xs font-bold rounded-full flex items-center justify-center"> {count} </span> )} </button> </div> </div> {cartOpen && <CartDrawer onClose={() => setCartOpen(false)} />} </header> ); } // Cart Drawer Component function CartDrawer({ onClose }: { onClose: () => void }) { const { items, total, updateQuantity, removeItem } = useCart(); return ( <div className="fixed inset-0 z-50"> <div className="absolute inset-0 bg-black/50" onClick={onClose} /> <div className="absolute right-0 top-0 h-full w-full max-w-md bg-white dark:bg-slate-900 shadow-xl"> <div className="flex items-center justify-between p-6 border-b border-slate-200 dark:border-slate-800"> <h2 className="text-lg font-semibold text-slate-900 dark:text-white">Shopping Cart</h2> <button onClick={onClose} className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg"> <CloseIcon /> </button> </div> <div className="p-6 space-y-4 max-h-[calc(100vh-200px)] overflow-y-auto"> {items.map((item) => ( <div key={item.id} className="flex gap-4"> <div className="w-20 h-20 rounded-xl bg-slate-100 dark:bg-slate-800 overflow-hidden"> <img src={item.image} alt={item.name} className="w-full h-full object-cover" /> </div> <div className="flex-1"> <h3 className="font-medium text-slate-900 dark:text-white">{item.name}</h3> <p className="text-slate-600 dark:text-slate-400">${item.price}</p> <div className="flex items-center gap-2 mt-2"> <button onClick={() => updateQuantity(item.id, item.quantity - 1)} className="w-8 h-8 rounded-lg border border-slate-200 dark:border-slate-700" > - </button> <span className="w-8 text-center">{item.quantity}</span> <button onClick={() => updateQuantity(item.id, item.quantity + 1)} className="w-8 h-8 rounded-lg border border-slate-200 dark:border-slate-700" > + </button> </div> </div> <button onClick={() => removeItem(item.id)} className="text-slate-400 hover:text-red-500"> <TrashIcon /> </button> </div> ))} </div> <div className="absolute bottom-0 left-0 right-0 p-6 border-t border-slate-200 dark:border-slate-800"> <div className="flex items-center justify-between mb-4"> <span className="text-slate-600 dark:text-slate-400">Subtotal</span> <span className="text-xl font-bold text-slate-900 dark:text-white">${total}</span> </div> <Link href="/checkout"> <button className="w-full py-3 bg-cyan-600 hover:bg-cyan-700 text-white font-medium rounded-xl"> Checkout </button> </Link> </div> </div> </div> ); } // Icon Components function SearchIcon() { return <svg className="w-5 h-5 text-slate-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>; } function UserIcon() { return <svg className="w-5 h-5 text-slate-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>; } function CartIcon() { return <svg className="w-5 h-5 text-slate-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" /></svg>; } function CloseIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>; } function TrashIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>; }

Customization Guide

Brand Colors

Update the color scheme to match your brand identity.

customization.tsx
// Replace cyan with your brand color
// In tailwind.config.ts:
colors: {
  brand: {
    50: '#f0f9ff',
    500: '#0ea5e9',
    600: '#0284c7',
    700: '#0369a1',
  }
}

// Then update classes:
// bg-cyan-600 → bg-brand-600

Product Card Styles

Customize product card appearance and hover effects.

customization.tsx
// Add badges, quick-add buttons, etc.
<div className="group relative">
  {product.isNew && (
    <span className="absolute top-2 left-2 px-2 py-1 bg-emerald-500 text-white text-xs rounded-full">
      New
    </span>
  )}
  {product.discount && (
    <span className="absolute top-2 right-2 px-2 py-1 bg-red-500 text-white text-xs rounded-full">
      -{product.discount}%
    </span>
  )}
  {/* Quick add button on hover */}
  <button className="absolute bottom-4 left-1/2 -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity">
    Add to Cart
  </button>
</div>

Navigation Structure

Customize the header navigation and categories.

customization.tsx
const categories = [
  { name: "New Arrivals", href: "/new" },
  { name: "Women", href: "/women",
    subcategories: ["Dresses", "Tops", "Pants"]
  },
  { name: "Men", href: "/men",
    subcategories: ["Shirts", "Jackets", "Shoes"]
  },
  { name: "Sale", href: "/sale", highlight: true },
];

Payment Integration

Connect with Stripe, PayPal, or other payment providers.

customization.tsx
// Example Stripe integration
import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY!);

async function handleCheckout() {
  const stripe = await stripePromise;
  const { sessionId } = await fetch('/api/checkout', {
    method: 'POST',
    body: JSON.stringify({ items: cartItems }),
  }).then(r => r.json());

  await stripe?.redirectToCheckout({ sessionId });
}

Ready to Launch Your Store?

Copy this template, customize it to your brand, and start selling. All the components you need are included.