In the precise navigation systems of Metropolis Quantum 2150, where every broken link can lead to catastrophe, you will learn about Typed Routes - an experimental Next.js feature that brings type safety to every path in your application.
Without Typed Routes, paths are just plain strings:
1// Easy to typo - no validation in TypeScript
2<Link href="/prodcuts">Products</Link> // Typo!
3<Link href="/users/123/setings">Settings</Link> // Another typo!
4
5// No parameter checking
6redirect('/products/' + productId); // What if productId is undefined?Typed Routes automatically generates types for every path in your application.
1// next.config.js
2module.exports = {
3 experimental: {
4 typedRoutes: true,
5 },
6};After enabling and running
next dev or next build, Next.js will generate a .next/types/link.d.ts file containing the types.1import Link from 'next/link';
2
3function Navigation() {
4 return (
5 <nav>
6 {/* Correct paths - TypeScript OK */}
7 <Link href="/">Home</Link>
8 <Link href="/products">Products</Link>
9 <Link href="/about">About</Link>
10
11 {/* Invalid path - TypeScript Error! */}
12 <Link href="/prodcuts">Products</Link>
13 {/* Type '"prodcuts"' is not assignable to type 'Route' */}
14 </nav>
15 );
16}1// For the structure: app/products/[id]/page.tsx
2<Link href="/products/123">Product 123</Link> // OK
3
4// For the structure: app/users/[userId]/posts/[postId]/page.tsx
5<Link href="/users/456/posts/789">Post</Link> // OK1'use client';
2
3import { useRouter } from 'next/navigation';
4
5function ProductActions({ productId }: { productId: string }) {
6 const router = useRouter();
7
8 const handleEdit = () => {
9 // TypeScript validates the path
10 router.push(`/products/${productId}/edit`);
11 };
12
13 const handleDelete = () => {
14 // Correct
15 router.push('/products');
16 };
17
18 const handleBroken = () => {
19 // TypeScript Error if /prodcts does not exist
20 router.push('/prodcts');
21 };
22
23 return (
24 <div>
25 <button onClick={handleEdit}>Edit</button>
26 <button onClick={handleDelete}>Delete</button>
27 </div>
28 );
29}1import { redirect } from 'next/navigation';
2
3async function ProtectedPage() {
4 const session = await getSession();
5
6 if (!session) {
7 // TypeScript validates the path
8 redirect('/login');
9 }
10
11 if (!session.isVerified) {
12 // Correct
13 redirect('/verify-email');
14 }
15
16 return <Dashboard />;
17}Next.js generates a
Route type you can use:1import type { Route } from 'next';
2
3// Function accepting only valid paths
4function navigateTo(path: Route) {
5 window.location.href = path;
6}
7
8navigateTo('/products'); // OK
9navigateTo('/prodcuts'); // Error1import type { Route } from 'next';
2
3// Function generating a product link
4function getProductUrl(id: string): Route {
5 return `/products/${id}` as Route;
6}
7
8// Usage
9<Link href={getProductUrl('123')}>Product</Link>1import Link from 'next/link';
2
3function ProductFilters() {
4 return (
5 <div>
6 {/* Path with query params */}
7 <Link
8 href={{
9 pathname: '/products',
10 query: { category: 'electronics', sort: 'price' },
11 }}
12 >
13 Electronics (sort by price)
14 </Link>
15
16 {/* Or as a string */}
17 <Link href="/products?category=electronics&sort=price">
18 Electronics
19 </Link>
20 </div>
21 );
22}1// types/navigation.ts
2import type { Route } from 'next';
3
4export interface NavigationItem {
5 label: string;
6 href: Route;
7 icon?: React.ReactNode;
8}
9
10export const mainNavigation: NavigationItem[] = [
11 { label: 'Home', href: '/' },
12 { label: 'Products', href: '/products' },
13 { label: 'Categories', href: '/categories' },
14 { label: 'About', href: '/about' },
15 { label: 'Contact', href: '/contact' },
16];
17
18export const userNavigation: NavigationItem[] = [
19 { label: 'Profile', href: '/account/profile' },
20 { label: 'Orders', href: '/account/orders' },
21 { label: 'Settings', href: '/account/settings' },
22];1// components/MainNav.tsx
2import Link from 'next/link';
3import { mainNavigation } from '@/types/navigation';
4
5export function MainNav() {
6 return (
7 <nav className="flex gap-6">
8 {mainNavigation.map((item) => (
9 <Link
10 key={item.href}
11 href={item.href} // TypeScript knows this is a valid path
12 className="hover:text-blue-500"
13 >
14 {item.label}
15 </Link>
16 ))}
17 </nav>
18 );
19}1// components/Breadcrumbs.tsx
2import Link from 'next/link';
3import type { Route } from 'next';
4
5interface BreadcrumbItem {
6 label: string;
7 href?: Route;
8}
9
10interface BreadcrumbsProps {
11 items: BreadcrumbItem[];
12}
13
14export function Breadcrumbs({ items }: BreadcrumbsProps) {
15 return (
16 <nav aria-label="Breadcrumb" className="flex items-center gap-2 text-sm">
17 {items.map((item, index) => (
18 <span key={index} className="flex items-center gap-2">
19 {index > 0 && <span className="text-gray-400">/</span>}
20 {item.href ? (
21 <Link href={item.href} className="text-blue-600 hover:underline">
22 {item.label}
23 </Link>
24 ) : (
25 <span className="text-gray-600">{item.label}</span>
26 )}
27 </span>
28 ))}
29 </nav>
30 );
31}
32
33// Usage
34<Breadcrumbs
35 items={[
36 { label: 'Home', href: '/' },
37 { label: 'Products', href: '/products' },
38 { label: 'Electronics', href: '/products?category=electronics' },
39 { label: 'Smartphone X' }, // Last item without a link
40 ]}
41/>1// TypeScript cannot validate the value of a dynamic segment
2const id = getUserInput();
3<Link href={`/products/${id}`}>Product</Link> // Accepts any string
4
5// You can use a type assertion if you're sure
6<Link href={`/products/${id}` as Route}>Product</Link>1// External URLs are not Route
2<Link href="https://google.com">Google</Link> // Error
3
4// Use a plain <a> for external links
5<a href="https://google.com">Google</a>1// For app/docs/[...slug]/page.tsx
2// TypeScript checks the prefix but not the full path
3<Link href="/docs/getting-started/installation">Docs</Link> // OK1// next.config.js
2module.exports = {
3 experimental: {
4 typedRoutes: true,
5 },
6};1npm run dev
2# or
3npm run build1# Check for errors
2npm run type-check
3
4# Common issues:
5# - Typos in paths
6# - Non-existent pages
7# - External links inside <Link>If the types are not working correctly:
1# Remove generated types and regenerate
2rm -rf .next/types
3npm run devCheck the generated file:
1// .next/types/link.d.ts
2// This file contains all the generated Route typesTyped Routes in Next.js provide:
In the precise systems of Metropolis Quantum 2150, where every navigation error can cost millions, Typed Routes are an essential safeguard for every professional Next.js application!