Floating Cart
A sleek, animated shopping cart widget with expandable drawer, item management, and smooth micro-interactions for e-commerce applications.
Installation
Backend Integration
The cart widget works with your existing e-commerce backend. Here's how to integrate it with common patterns:
- Cart State Management: /hooks/useCart.ts
1import { useState, useEffect } from "react";2 3interface CartItem {4 id: number;5 name: string;6 price: number;7 quantity: number;8 image: string;9}10 11export function useCart() {12 const [items, setItems] = useState<CartItem[]>([]);13 14 // Load cart from localStorage on mount15 useEffect(() => {16 const savedCart = localStorage.getItem("cart");17 if (savedCart) {18 setItems(JSON.parse(savedCart));19 }20 }, []);21 22 // Save cart to localStorage when items change23 useEffect(() => {24 localStorage.setItem("cart", JSON.stringify(items));25 }, [items]);26 27 const addItem = (product: Omit<CartItem, "quantity">) => {28 setItems((prev) => {29 const existing = prev.find((item) => item.id === product.id);30 if (existing) {31 return prev.map((item) =>32 item.id === product.id33 ? { ...item, quantity: item.quantity + 1 }34 : item,35 );36 }37 return [...prev, { ...product, quantity: 1 }];38 });39 };40 41 const removeItem = (id: number) => {42 setItems((prev) => prev.filter((item) => item.id !== id));43 };44 45 const updateQuantity = (id: number, quantity: number) => {46 if (quantity <= 0) {47 removeItem(id);48 return;49 }50 setItems((prev) =>51 prev.map((item) => (item.id === id ? { ...item, quantity } : item)),52 );53 };54 55 const clearCart = () => setItems([]);56 57 return {58 items,59 addItem,60 removeItem,61 updateQuantity,62 clearCart,63 itemCount: items.reduce((sum, item) => sum + item.quantity, 0),64 subtotal: items.reduce((sum, item) => sum + item.price * item.quantity, 0),65 };66}
- Checkout Integration: /app/api/checkout/route.ts
1import { NextResponse } from "next/server";2import Stripe from "stripe";3 4const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {5 apiVersion: "2023-10-16",6});7 8export async function POST(request: Request) {9 try {10 const { items, customerEmail } = await request.json();11 12 // Create Stripe checkout session13 const session = await stripe.checkout.sessions.create({14 payment_method_types: ["card"],15 line_items: items.map((item: any) => ({16 price_data: {17 currency: "usd",18 product_data: {19 name: item.name,20 images: [item.image],21 },22 unit_amount: Math.round(item.price * 100),23 },24 quantity: item.quantity,25 })),26 mode: "payment",27 success_url: `${process.env.NEXT_PUBLIC_URL}/success`,28 cancel_url: `${process.env.NEXT_PUBLIC_URL}/cart`,29 customer_email: customerEmail,30 });31 32 return NextResponse.json({ sessionId: session.id });33 } catch (error) {34 console.error("Checkout error:", error);35 return NextResponse.json(36 { error: "Failed to create checkout session" },37 { status: 500 },38 );39 }40}
- Environment Variables
Add these to .env.local:
1STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_public_key2STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key3NEXT_PUBLIC_URL=http://localhost:3000
Props
1type CartItem = {2 id: number;3 name: string;4 price: number;5 quantity: number;6 image: string;7};
Features
- Floating Design: Fixed position widget that doesn't interfere with page content
- Expandable Drawer: Smooth slide-out drawer with backdrop overlay
- Item Management: Add, remove, and update quantities with animated feedback
- Real-time Updates: Live subtotal calculation and item count badge
- Smooth Animations: Framer Motion powered micro-interactions throughout
- Click Outside: Automatically closes when clicking outside the drawer
- Keyboard Support: ESC key to close, full accessibility support
- Responsive Design: Adapts perfectly to mobile and desktop screens
- Dark Mode: Full dark mode support with proper contrast ratios
- Touch Friendly: Large touch targets optimized for mobile interaction
- Loading States: Built-in loading indicators for async operations
- TypeScript: Complete TypeScript support with proper type definitions
Animation Details
The component uses Framer Motion for fluid animations:
- Drawer Entrance: Spring-based scale and fade animation with backdrop
- Item Stagger: Sequential item animations with custom delay timing
- Quantity Controls: Smooth scale animations on button interactions
- Hover Effects: Subtle translate and background color transitions
- Remove Actions: Rotate and scale animations for delete interactions
- Badge Animations: Scale-in animation for cart count indicator
- Button States: Loading spinners and state transitions
Customization
1<FloatingCartWidget2 items={cartItems}3 onViewCart={() => router.push('/cart')}4 onCheckout={handleCheckout}5/>6 7// Custom CSS for positioning8.floating-cart-custom {9 @apply fixed right-4 bottom-4 md:right-8 md:bottom-8;10}
1// Extend the component for custom item display2const CustomFloatingCart = ({ items, ...props }) => {3 const formatPrice = (price) => {4 return new Intl.NumberFormat("en-US", {5 style: "currency",6 currency: "USD",7 }).format(price);8 };9 10 return (11 <FloatingCartWidget12 items={items.map((item) => ({13 ...item,14 displayPrice: formatPrice(item.price),15 }))}16 {...props}17 />18 );19};
1import { useSelector, useDispatch } from "react-redux";2import { removeFromCart, updateQuantity } from "./cartSlice";3 4export function ConnectedFloatingCart() {5 const dispatch = useDispatch();6 const cartItems = useSelector((state) => state.cart.items);7 8 return (9 <FloatingCartWidget10 items={cartItems}11 onViewCart={() => router.push("/cart")}12 onCheckout={() => router.push("/checkout")}13 />14 );15}
Best Practices
- Use for cart items to prevent unnecessary re-renders
1React.memo
- Implement debounced quantity updates for better UX
- Consider virtualization for large cart item lists
- Ensure proper ARIA labels for all interactive elements
- Support keyboard navigation throughout the component
- Provide screen reader announcements for cart updates
- Show loading states during async operations
- Provide clear feedback for all user actions
- Consider auto-save functionality for cart persistence
- Implement undo functionality for item removals