URL structure and Next.js routing rules for SEO. Use when creating new routes, pages, modifying URL patterns, adding dynamic segments, or working with pagination and query parameters.
Technical rules for URL patterns and routing in a Next.js e-commerce site.
A common e-commerce URL pattern is /{lang}-{COUNTRY}/{section}/{id}/{slug}. Adapt to your project's actual structure:
Example (adapt paths to your project):
/en-US//en-US/categories/electronics/en-US/categories/electronics/wireless-headphones/en-US/products/123/blue-widget-pro-3000/en-US/contact-us/products/123 without a locale/ to the detected or default locale (see seo-hreflang skill for middleware pattern)en-GB, de-DE, fr-CH// GOOD — locale from route params
// [locale]/products/[id]/[slug]/page.tsx
export default function ProductPage({ params }: { params: { locale: string; id: string; slug: string } }) {
// locale is dynamic
}
// BAD — hardcoded locale
const url = '/en-US/products/' + id; // Breaks for other locales
blue-widget-pro-3000, never Blue-Widget-Pro-3000wireless-headphones, not wireless_headphones or wirelessheadphonesPrefer path-based segments for subcategories:
// PREFERRED — path-based
/en-US/categories/electronics/wireless-headphones
// ACCEPTABLE — query-based (common in existing codebases)
/en-US/categories/electronics?type=wireless-headphones
Path-based URLs are better for SEO because:
?page=N query parameter for paginated pages?page=1 — the canonical should be the base URL without the page param?page=2 points to ?page=2)rel="next" and rel="prev" are not used by Google (dropped in 2019). Bing and some other engines still support them. Including them is low-effort and harmless, but don't rely on them for Google — proper canonical URLs and sitemap inclusion are more important for paginated content.import { SITE_URL } from '@/config/site';
export async function generateMetadata({ params, searchParams }: Props): Promise<Metadata> {
const page = Number(searchParams.page) || 1;
const basePath = `${SITE_URL}/${params.locale}/categories/${params.slug}`;
return {
alternates: {
canonical: page === 1 ? basePath : `${basePath}?page=${page}`,
},
other: {
...(page > 1 && { 'prev': page === 2 ? basePath : `${basePath}?page=${page - 1}` }),
...(hasNextPage && { 'next': `${basePath}?page=${page + 1}` }),
},
};
}
Disallow: /*/search/noindex to search result pages as a fallback — if robots.txt is ever changed or removed, the noindex tag ensures these pages stay out of the index. Note: while robots.txt is active, Google cannot see the noindex tag (it blocks crawling entirely), so both protections do not work simultaneously.// Search page (any router)
export const metadata: Metadata = {
robots: { index: false, follow: true }, // noindex, follow
};
Why: Search results pages create near-infinite URL combinations, which wastes crawl budget and creates thin content.
// next.config.js
module.exports = {
trailingSlash: false, // or true — pick one and stick with it
};
[id] and [slug] segments for dynamic contentgenerateStaticParams to pre-render known pages at build time:export async function generateStaticParams() {
const products = await getAllProducts();
return products.map(product => ({
locale: product.locale,
id: product.id,
slug: product.slug,
}));
}
revalidate) for pages that update frequently (prices, stock):export const revalidate = 300; // Regenerate every 5 minutes
?color=red&size=large&weight=5kg should:
noindex, follow to prevent indexing while allowing link discovery?type=wireless), keep it indexed?sort=price-asc) — the canonical should always point to the base URL/{lang}-{COUNTRY}/en-US?page=1 — the canonical for page 1 is the clean URL without pagination