Overview
The OrderSummary component displays a summary of cart items with individual item prices, subtotal, shipping information, and total. It’s used in checkout flows to show what the customer is purchasing.
Props
This component does not accept props. It retrieves cart data directly from the Zustand store:
src/components/orderSummary/OrderSummary.jsx:4-5
export const OrderSummary = () => {
const cart = useCartStore (( state ) => state . cart );
Usage
Shipping Page
Payment Page
import { OrderSummary } from "../../components/orderSummary/OrderSummary" ;
export const CheckoutShipping = () => {
return (
< div className = "container" >
< div className = "row" >
< div className = "col-md-6" >
{ /* Shipping form */ }
</ div >
< div className = "col-md-5 offset-md-1" >
< OrderSummary />
</ div >
</ div >
</ div >
);
};
Features
Cart Calculations
Automatically calculates totals from cart state:
src/components/orderSummary/OrderSummary.jsx:7-12
const total = cart . reduce (
( acc , item ) => acc + item . price * item . qty ,
0
);
const totalItems = cart . reduce (( acc , item ) => acc + item . qty , 0 );
Empty State
Displays a message when cart is empty:
src/components/orderSummary/OrderSummary.jsx:14-22
if ( cart . length === 0 ) {
return (
< div className = "order-summary-card card" >
< div className = "order-summary-empty" >
< p className = "order-summary-empty-message" > Your cart is empty </ p >
</ div >
</ div >
);
}
Item List with Truncation
Displays each cart item with truncated titles for long product names:
src/components/orderSummary/OrderSummary.jsx:28-44
{ cart . map (( item ) => (
< div key = { item . id } className = "order-summary-item" >
< div className = "order-summary-item-info" >
< span className = "order-summary-item-title" >
{ item . title . length > 35
? ` ${ item . title . substring ( 0 , 35 ) } ...`
: item . title }
</ span >
< span className = "order-summary-item-quantity" >
Amount: { item . qty }
</ span >
</ div >
< span className = "order-summary-item-price" >
$ { ( item . price * item . qty ). toFixed ( 2 ) }
</ span >
</ div >
))}
Product titles longer than 35 characters are automatically truncated with an ellipsis to maintain a clean layout.
Pricing Breakdown
Subtotal
Displays item count and subtotal:
src/components/orderSummary/OrderSummary.jsx:48-53
< div className = "order-summary-row" >
< span className = "order-summary-label" >
Subtotal ( { totalItems } { totalItems === 1 ? "item" : "items" } )
</ span >
< span > $ { total . toFixed ( 2 ) } </ span >
</ div >
Shipping
Shows free shipping:
src/components/orderSummary/OrderSummary.jsx:55-58
< div className = "order-summary-row" >
< span className = "order-summary-label" > Shipping </ span >
< span className = "order-summary-shipping" > Free </ span >
</ div >
Total
Displays final total:
src/components/orderSummary/OrderSummary.jsx:62-67
< div className = "order-summary-total" >
< span > Total </ span >
< span className = "order-summary-total-amount" >
$ { total . toFixed ( 2 ) }
</ span >
</ div >
Component Structure
< div className = "order-summary-card card" >
< h5 className = "order-summary-title" > Order Summary </ h5 >
{ /* Item list */ }
{ cart . map (( item ) => (
< div key = { item . id } className = "order-summary-item" >
{ /* Item details */ }
</ div >
)) }
< hr className = "order-summary-divider" />
{ /* Subtotal */ }
< div className = "order-summary-row" > ... </ div >
{ /* Shipping */ }
< div className = "order-summary-row" > ... </ div >
< hr className = "order-summary-divider" />
{ /* Total */ }
< div className = "order-summary-total" > ... </ div >
</ div >
Styling
The component uses these CSS classes from OrderSummary.css:
.order-summary-card - Main card container
.order-summary-title - “Order Summary” heading
.order-summary-item - Individual item row
.order-summary-item-info - Item title and quantity container
.order-summary-item-title - Product title
.order-summary-item-quantity - Quantity text
.order-summary-item-price - Item total price
.order-summary-divider - Horizontal separator
.order-summary-row - Subtotal and shipping rows
.order-summary-label - Label text
.order-summary-shipping - Free shipping text
.order-summary-total - Total row with emphasis
.order-summary-total-amount - Final total amount
.order-summary-empty - Empty state container
.order-summary-empty-message - Empty message text
Real-World Integration
From the CheckoutShipping page:
pages/checkoutShipping/CheckoutShipping.jsx
import { useNavigate } from "react-router-dom" ;
import { CheckoutStepper } from "../../components/checkoutStepper/CheckoutStepper" ;
import { OrderSummary } from "../../components/orderSummary/OrderSummary" ;
import { useUserStore } from "../../store/useUserStore" ;
export const CheckoutShipping = () => {
const navigate = useNavigate ();
const shipping = useUserStore (( state ) => state . shipping );
const setShipping = useUserStore (( state ) => state . setShipping );
const handleSubmit = ( e ) => {
e . preventDefault ();
navigate ( "/checkout/payment" );
};
return (
< div className = "container shipping-container" >
< CheckoutStepper step = { 2 } />
< h2 className = "shipping-title" > Shipping Information </ h2 >
< div className = "row" >
< div className = "col-md-6 shipping-form-col" >
< form onSubmit = { handleSubmit } className = "shipping-form" >
{ /* Form inputs */ }
</ form >
</ div >
< div className = "col-md-5 offset-md-1 shipping-summary-col" >
< OrderSummary />
</ div >
</ div >
</ div >
);
};
Item Structure
Each cart item in the store should have this structure:
interface CartItem {
id : string | number ;
title : string ;
price : number ;
qty : number ;
thumbnail ?: string ; // Not used in OrderSummary, but part of cart items
}
The OrderSummary automatically updates when cart state changes, making it perfect for displaying real-time totals during the checkout process.
Plural Handling
The component correctly handles singular/plural item counts:
Subtotal ({ totalItems } { totalItems === 1 ? "item" : "items" })
Examples:
“Subtotal (1 item)”
“Subtotal (3 items)”