Next.js 15 uses the App Router with file-based routing. This section covers how to structure your frontend, use Server and Client Components, and implement data fetching patterns.
App Router Architecture
Next.js 15 uses the App Router with file-based routing. The file structure in the app directory determines the routes:
Route Groups
Route groups (folders in parentheses) organize routes without affecting the URL:
(admin): Admin routes with authentication – All routes under/admin/*(public): Public-facing routes – Shop, product, cart, checkout pages
Route Structure
app/
├── (admin)/admin/ # /admin/*
│ ├── products/ # /admin/products
│ ├── categories/ # /admin/categories
│ └── tags/ # /admin/tags
└── (public)/ # Public routes
├── page.jsx # / (homepage)
├── shop/ # /shop
├── product/[slug]/ # /product/slug
├── cart/ # /cart
├── checkout/ # /checkout
└── account/ # /accountServer Components vs Client Components
Next.js 15 distinguishes between Server Components (default) and Client Components:
Server Components (Default)
- Run on the server only
- No JavaScript sent to client
- Can directly access databases and APIs
- Better for SEO and performance
- Can use async/await
- Cannot use browser APIs or hooks
Client Components
- Run in the browser
- Require
"use client"directive - Can use React hooks
- Can use browser APIs
- Interactive UI with state
- Event handlers and user interactions
Server Component Example
import { WooCommerceAPI } from "@/lib/woocommerce"; // Import WooCommerce API
export default async function ShopPage() { // Server component to fetch products
const { products } = await WooCommerceAPI.getProducts();
return (
{products.map(product => (
))}
);
}Benefits: Server Components are rendered on the server, so the HTML is sent directly to the browser. This improves SEO and initial page load performance.
Client Component Example
"use client";
import { useCart } from "@/contexts/CartContext"; // Import Cart context
export default function AddToCartButton({ productId }) { // Client component to add to cart
const { addToCart } = useCart(); // Get add to cart function from Cart context
return (
addToCart(productId, 1)}> // Add to cart with quantity 1 to the product with the given productId
Add to Cart
);
}Data Fetching Patterns
Next.js 15 provides multiple ways to fetch data. Choose the pattern that best fits your use case:
1. Server-Side Fetching (Recommended)
export default async function ProductPage({ params }) { // Server component to fetch product by slug
const product = await WooCommerceAPI.getProductBySlug(params.slug); // Get product by slug
if (!product) notFound(); // If product not found, show 404 page
return ; // Return product details component
}2. API Routes + Client Fetching
"use client";
useEffect(() => {
fetch("/api/products")
.then(res => res.json())
.then(data => setProducts(data));
}, []);3. Server Actions (Next.js 15)
// app/actions.js
"use server";
export async function getProducts() { // Server action to fetch products
return await WooCommerceAPI.getProducts();
}
// In component
const data = await getProducts();Dynamic Routes
Use square brackets for dynamic route segments:
// app/product/[slug]/page.jsx
export default async function ProductPage({ params }) { // Server component to fetch product by slug
const product = await WooCommerceAPI.getProductBySlug(params.slug); // Get product by slug
if (!product) notFound(); // If product not found, show 404 page
return ;
}Layouts
Layouts wrap pages and persist across navigation:
// app/layout.js
export default function RootLayout({ children }) { // Root layout to wrap pages and persist across navigation
return (
{children}
);
}Metadata and SEO
export async function generateMetadata({ params }) {
const product = await WooCommerceAPI.getProductBySlug(params.slug); // Get product by slug
if (!product) notFound(); // If product not found, show 404 page
return {
title: product.name, // Set product name as title
description: product.short_description, // Set product short description as description
description: product.short_description,
};
}Loading and Error States
// loading.jsx
export default function Loading() { // Loading component to show loading state
return Loading...;
}
// error.jsx
"use client";
export default function Error({ error, reset }) { // Error component to show error state
return (
{error.message}
Try again // Try again button to reset the error
);
}Best Practices
- Use Server Components by default – Only use Client Components when necessary
- Fetch data in Server Components – Better performance and SEO
- Keep Client Components small – Move logic to Server Components when possible
- Use dynamic imports – Code split heavy components
- Optimize images – Use Next.js Image component
- Add metadata – Improve SEO with proper metadata
Next Steps: Learn about API Routes and Components to build interactive features.