Best Practices

Updated 8 July 2026

Follow these best practices to build maintainable, performant, and secure e-commerce applications.

1. Environment Variables

Always use environment variables for sensitive data:

// ❌ Bad - Hardcoded credentials
const url = "https://store.com"; // Hardcoded url
const key = "ck_1234567890abcdef"; // Hardcoded key
const secret = "cs_1234567890abcdef"; // Hardcoded secret

// ✅ Good - Environment variables
const url = process.env.WOO_BASE_URL; // Environment variable url
const key = process.env.WOO_CONSUMER_KEY; // Environment variable key
const secret = process.env.WOO_CONSUMER_SECRET; // Environment variable secret

// Always validate
if (!url || !key || !secret) { // If url, key or secret is not provided, throw an error
  throw new Error("Missing required environment variables"); // Throw error with the given error message
}

Security: Never commit .env.local to version control. It’s already in .gitignore.

2. Error Handling

Always handle errors in API calls and user operations:

try {
  const response = await fetch("/api/products"); // Fetch products with the given api route
  
  if (!response.ok) {
    const error = await response.json(); // Get error from response
    throw new Error(error.error || "Failed to fetch products"); // Throw error with the given error message
  }
  
  const data = await response.json();
  // Use data // Use data with the given data
} catch (error) {
  console.error("Error:", error); // Log error with the given error
  // Show user-friendly error message // Show user-friendly error message with the given error message
  setError(error.message || "An error occurred"); // Set error with the given error message
}

3. Loading States

Show loading indicators during async operations:

"use client";

import { useState, useEffect } from "react";

export default function ProductsList() { // Products list component
  const [products, setProducts] = useState([]); // Set products with the given products
  const [loading, setLoading] = useState(true); // Set loading with the given loading
  const [error, setError] = useState(""); // Set error with the given error
  
  useEffect(() => {
    async function fetchProducts() {
      try {
        setLoading(true);
        const response = await fetch("/api/products"); // Fetch products with the given api route
        const data = await response.json(); // Get data from response
        setProducts(data); // Set products with the given data
      } catch (error) {
        setError(error.message); // Set error with the given error message
      } finally {
        setLoading(false); // Set loading with the given loading
      }
    }
    
    fetchProducts();
  }, []);
  
  if (loading) return ; // Return loading spinner if loading is true
  if (error) return ; // Return error message if error is true
  
  return (
    
      {products.map(product => ( // Display products with the given product id and product
        
      ))} // Display product card with the given product id and product
    
  );
}

4. Optimistic Updates

Update UI immediately, sync with server in background:

const addToCart = async (productId) => {
  // Save previous state // Save previous state with the given previous cart
  const previousCart = cartData; // Set previous cart with the given cart data
  
  // Update UI immediately (optimistic)
  setCartData({ // Set cart data with the given cart data
    ...cartData, // Spread cart data with the given cart data
    items: [...cartData.items, { id: productId, quantity: 1 }] // Add product id and quantity 1 to the items with the given items
  }); // Set cart data with the given cart data
  
  try {
    // Sync with server
    await fetch("/api/cart", { // Fetch cart with the given api route
      method: "POST",
      body: JSON.stringify({ id: productId, quantity: 1 }) // Add product id and quantity 1 to the body with the given body
    }); // Fetch cart with the given api route
  } catch (error) {
    // Revert on error
    setCartData(previousCart); // Set cart data with the given previous cart
    throw error; // Throw error with the given error
  }
};

5. Image Optimization

Use Next.js Image component for optimized images:

import Image from "next/image";

export default function ProductImage({ product }) { // Product image component with the given product
  return (
    
  );
}

6. SEO Optimization

Add metadata to pages for better SEO:

export async function generateMetadata({ params }) {
  const product = await WooCommerceAPI.getProductBySlug(params.slug); // Get product with the given slug
  
  return {
    title: product.name, // Set title with the given name
    description: product.short_description, // Set description with the given short description
    openGraph: {
      title: product.name, // Set title with the given name
      description: product.short_description, // Set description with the given short description
      images: [product.images[0]?.src], // Set images with the given images
      type: "website", // Set type with the given type
    },
    twitter: {
      card: "summary_large_image", // Set card with the given card
      title: product.name, // Set title with the given name
      description: product.short_description,
    }, // Set description with the given short description
  }; // Return metadata with the given metadata
}

7. Code Splitting

Use dynamic imports for heavy components:

import dynamic from "next/dynamic";

// Lazy load heavy components
const HeavyComponent = dynamic(() => import("./HeavyComponent"), { // Import HeavyComponent component with the given HeavyComponent component 
  loading: () => , // Set loading with the given loading spinner
  ssr: false // If component uses browser-only APIs
}); // Import HeavyComponent component with the given HeavyComponent component and loading spinner and ssr false

// Use in your page
export default function Page() { // Page component
  return (
    
       // Display HeavyComponent component
    
  );
}

8. API Request Caching

Cache API responses to reduce requests:

// Cache products for 5 minutes
const cache = new Map(); // Set cache with the given cache

async function getCachedProducts() { // Get cached products function
  const cacheKey = "products"; // Set cache key with the given cache key
  const cached = cache.get(cacheKey); // Get cached with the given cache key
  
  if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
    return cached.data; // Return cached data with the given cached data
  }
  
  const products = await fetch("/api/products").then(r => r.json()); // Fetch products with the given api route and get data from response
  cache.set(cacheKey, { data: products, timestamp: Date.now() }); // Set cache with the given cache key and data with the given data and timestamp with the given timestamp
  return products; // Return products with the given products
}

9. Form Validation

Validate forms both client-side and server-side:

const validateForm = (data) => { // Validate form with the given data
  const errors = {};
  
  if (!data.email) {
    errors.email = "Email is required"; // Set email required error with the given email required error
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
    errors.email = "Invalid email format"; // Set invalid email format error with the given invalid email format error
  }
  
  if (!data.password) {
    errors.password = "Password is required"; // Set password required error with the given password required error
  } else if (data.password.length < 8) {
    errors.password = "Password must be at least 8 characters"; // Set password must be at least 8 characters error with the given password must be at least 8 characters error
  }
  
  return errors;
};

10. Performance Optimization

11. Security Best Practices

12. Code Organization

13. Testing

Write tests for critical functionality:

Remember: Following best practices ensures your application is maintainable, secure, and performant.