The order history page provides users with a comprehensive view of all their past purchases, including detailed breakdowns of items, quantities, and totals.
Overview
Order history features include:
Complete list of all past orders
Detailed item breakdown for each order
Order dates and unique identifiers
Total amounts with shipping information
Chronological display (newest first)
Accessing Order History
Users can view their order history at /orders after completing at least one purchase through the checkout flow.
Orders are stored in the user store with persistence, meaning order history survives browser restarts and remains available across sessions.
Order History Page
Component Structure
src/pages/orders/OrderHistory.jsx
import { useUserStore } from "../../store/useUserStore" ;
import { Package , ShoppingBag } from "lucide-react" ;
import { Link } from "react-router-dom" ;
export const OrderHistory = () => {
const orders = useUserStore (( state ) => state . orders );
if ( orders . length === 0 ) {
return (
< div className = "container orders-empty" >
< ShoppingBag size = { 48 } className = "orders-empty-icon" />
< h3 > No orders yet </ h3 >
< p > Your order history will appear here after your first purchase. </ p >
< Link to = "/" className = "orders-shop-btn" >
Start Shopping
</ Link >
</ div >
);
}
return (
< div className = "container orders-container" >
< h2 className = "orders-title" > Order History </ h2 >
{ /* Order list */ }
</ div >
);
};
Empty State
When users haven’t placed any orders, they see an inviting empty state:
Icon Large shopping bag icon (48px)
Message Clear message explaining the empty state
Call to Action “Start Shopping” button linking to catalog
src/pages/orders/OrderHistory.jsx
if ( orders . length === 0 ) {
return (
< div className = "container orders-empty" >
< ShoppingBag size = { 48 } className = "orders-empty-icon" />
< h3 > No orders yet </ h3 >
< p > Your order history will appear here after your first purchase. </ p >
< Link to = "/" className = "orders-shop-btn" >
Start Shopping
</ Link >
</ div >
);
}
Order Display
Order Card Layout
Each order is displayed in a detailed card showing all relevant information:
src/pages/orders/OrderHistory.jsx
< div className = "orders-list" >
{ [ ... orders ]. reverse (). map (( order ) => (
< div key = { order . id } className = "orders-card card" >
< div className = "orders-card-header" >
< div className = "orders-card-meta" >
< Package size = { 15 } />
< span className = "orders-id" > # { order . id } </ span >
</ div >
< span className = "orders-date" >
{new Date ( order . date ). toLocaleDateString ( "en-US" , {
year: "numeric" ,
month: "long" ,
day: "numeric" ,
}) }
</ span >
</ div >
{ /* Order items and totals */ }
</ div >
)) }
</ div >
Orders are displayed in reverse chronological order using [...orders].reverse(), showing the most recent orders first.
Each order card header displays:
Order ID
Unique 8-character identifier prefixed with #
Package Icon
Visual indicator using Lucide’s Package icon
Order Date
Formatted date (e.g., “March 9, 2026”)
Order Items List
All products in the order are displayed with details:
src/pages/orders/OrderHistory.jsx
< div className = "orders-items" >
{ order . items . map (( item ) => (
< div key = { item . id } className = "orders-summary-item" >
< img
src = { item . thumbnail }
alt = { item . title }
className = "orders-item-img"
/>
< div className = "orders-item-info" >
< span className = "orders-item-title" >
{ item . title . length > 40
? ` ${ item . title . substring ( 0 , 40 ) } ...`
: item . title }
</ span >
< span className = "orders-item-quantity" >
Amount: { item . qty }
</ span >
</ div >
< span className = "orders-item-price" >
$ { ( item . price * item . qty ). toFixed ( 2 ) }
</ span >
</ div >
)) }
</ div >
Thumbnail Product image for visual reference
Title Product name (truncated at 40 chars)
Quantity Number of units purchased
Subtotal Line total (price × quantity)
Long product titles are automatically truncated to 40 characters with ellipsis for clean display.
Order Totals
Financial Breakdown
Each order card displays a complete financial summary:
src/pages/orders/OrderHistory.jsx
< div className = "orders-card-footer" >
< div className = "orders-footer-row" >
< span className = "orders-footer-label" >
Subtotal ( { order . items . reduce (( a , i ) => a + i . qty , 0 ) } items)
</ span >
< span > $ { order . total . toFixed ( 2 ) } </ span >
</ div >
< div className = "orders-footer-row" >
< span className = "orders-footer-label" > Shipping </ span >
< span className = "orders-shipping-free" > Free </ span >
</ div >
< hr className = "orders-divider" />
< div className = "orders-footer-total" >
< span > Total </ span >
< span className = "orders-total-value" >
$ { order . total . toFixed ( 2 ) }
</ span >
</ div >
</ div >
Summary Components
Subtotal
Sum of all items with total item count display
Shipping
Displayed as “Free” in green text
Divider Line
Visual separator before final total
Grand Total
Final order amount in prominent styling
Order Data Structure
Order Object
Each order in the history has the following structure:
{
id : "ABC12345" , // 8-character unique ID
date : "2026-03-09T10:30:00Z" , // ISO 8601 timestamp
items : [ // Array of purchased products
{
id: 1 ,
title: "Product Name" ,
price: 29.99 ,
thumbnail: "https://..." ,
qty: 2
}
],
total : 59.98 // Total order amount
}
Order Creation
Orders are created during checkout payment processing:
src/pages/checkoutpayment/CheckoutPayment.jsx
const orderId = Math . random (). toString ( 36 ). substring ( 2 , 10 ). toUpperCase ();
addOrder ({
id: orderId ,
date: new Date (). toISOString (),
items: [ ... cart ],
total: cart . reduce (( acc , item ) => acc + item . price * item . qty , 0 ),
});
Order IDs are generated client-side using Math.random(). In production, use server-generated UUIDs or sequential IDs.
State Management
Order Storage
Orders are managed through the user store:
src/store/useUserStore.js
export const useUserStore = create (
persist (
( set ) => ({
orders: [],
addOrder : ( order ) =>
set (( state ) => ({
orders: [ ... state . orders , order ],
})),
}),
{
name: "user-checkout-storage" ,
},
),
);
Persistence
The persist middleware automatically saves orders to localStorage under the key user-checkout-storage, ensuring order history survives browser restarts.
Localized Date Display
Order dates are formatted using JavaScript’s toLocaleDateString:
src/pages/orders/OrderHistory.jsx
new Date ( order . date ). toLocaleDateString ( "en-US" , {
year: "numeric" ,
month: "long" ,
day: "numeric" ,
})
Example output: March 9, 2026
Calculations
Item Count
Total number of items in an order:
order . items . reduce (( acc , item ) => acc + item . qty , 0 )
Line Item Totals
Price for each product line:
( item . price * item . qty ). toFixed ( 2 )
Order Total
Stored during order creation:
cart . reduce (( acc , item ) => acc + item . price * item . qty , 0 )
All monetary values are formatted to 2 decimal places using .toFixed(2) for consistent currency display.
User Experience Features
Visual Hierarchy
Order Separation Each order in its own card with clear borders
Section Dividers Horizontal rules separate items from totals
Typography Varied font sizes emphasize important information
Icons Package and shopping bag icons add visual context
Responsive Layout
The order history page uses responsive container classes:
< div className = "container orders-container" >
{ /* Content adapts to screen size */ }
</ div >
Empty State UX
The empty state doesn’t just show “no orders” - it encourages users to start shopping with a clear call-to-action button.
Integration with Checkout
Order history is the final destination after successful checkout:
Complete Payment
User submits payment on checkout page
Create Order
System generates order with unique ID
Add to History
Order saved to user store with addOrder()
Clear Cart
Shopping cart is emptied
Navigate to Success
User sees success page with order ID
View History
User can navigate to order history to see all orders
Best Practices
Display Guidelines
Implementation Best Practices
Sorting
Always show newest orders first using .reverse()
Consider pagination for users with many orders
Data Display
Truncate long product titles for clean layout
Format all prices to 2 decimal places
Use consistent date formatting throughout
Use React keys properly (order.id for orders, item.id for items)
Consider lazy loading images for orders with many items
User Experience
Provide clear empty state with action button
Use icons for visual interest and clarity
Maintain consistent styling with checkout flow
Future Enhancements
Potential improvements for order history:
Order Search Search orders by ID, product name, or date range
Order Details Page Dedicated page for each order with more information
Reorder Functionality Quick reorder button to add items back to cart
Order Status Track shipping status and delivery updates
Current implementation stores orders client-side only. Production systems should use server-side storage with proper database persistence.