Next.js Linking
Introduction
Navigation is a fundamental aspect of any web application. In traditional websites, moving between pages often triggers a full page reload, resulting in a jarring user experience. Next.js, however, offers a solution that enables seamless client-side transitions between routes, making your application feel more like a single-page app while maintaining the benefits of server-rendered pages.
In this guide, we'll explore the various methods of linking and navigation in Next.js, from basic links to advanced programmatic navigation patterns.
The Link Component
Next.js provides a built-in Link
component that extends the HTML <a>
tag, enabling client-side navigation between pages in your application.
Basic Usage
To use the Link
component, first import it from 'next/link':
import Link from 'next/link';
function HomePage() {
return (
<div>
<h1>Welcome to my website</h1>
<Link href="/about">About Us</Link>
</div>
);
}
export default HomePage;
When a user clicks on "About Us", they'll be taken to the /about
page without a full page reload. Next.js automatically prefetches the linked page in the background when the link appears in the viewport, making navigation feel instantaneous.
Link Props
The Link
component accepts several props to customize its behavior:
<Link
href="/blog/post-1" // The destination path
as="/articles/my-post" // Optional path shown in the browser URL bar
replace={false} // If true, replaces current history entry instead of adding a new one
scroll={true} // Controls whether to scroll to top after navigation
prefetch={true} // Controls whether the link should be prefetched
locale="en-US" // For internationalized routes
>
Read my blog post
</Link>
Dynamic Routes
For dynamic routes, you can pass an object to the href
prop with pathname and query parameters:
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: 'my-post' },
}}
>
Read my blog post
</Link>
This will navigate to /blog/my-post
.
Styling Links
Since Next.js 13, the Link
component automatically adds the <a>
tag, so you can style it directly:
<Link href="/contact" className="primary-button">
Contact Us
</Link>
For older versions of Next.js, you need to manually include the <a>
tag:
<Link href="/contact">
<a className="primary-button">Contact Us</a>
</Link>
Linking to External Sites
For external links, you can use regular <a>
tags:
<a href="https://nextjs.org" target="_blank" rel="noopener noreferrer">
Learn more about Next.js
</a>
Active Links
Next.js doesn't provide a built-in way to style active links, but you can implement this functionality using the useRouter
hook:
import Link from 'next/link';
import { useRouter } from 'next/router';
function NavLink({ href, children }) {
const router = useRouter();
const isActive = router.pathname === href;
return (
<Link href={href}>
<a className={isActive ? 'active' : ''}>{children}</a>
</Link>
);
}
function Navigation() {
return (
<nav>
<NavLink href="/">Home</NavLink>
<NavLink href="/about">About</NavLink>
<NavLink href="/contact">Contact</NavLink>
</nav>
);
}
Programmatic Navigation
Sometimes you need to navigate users based on certain conditions or after completing an action (like form submission). For this, you can use the useRouter
hook:
import { useRouter } from 'next/router';
function LoginForm() {
const router = useRouter();
const handleSubmit = async (event) => {
event.preventDefault();
// Process login...
const success = await login(/* credentials */);
if (success) {
// Navigate to dashboard after successful login
router.push('/dashboard');
}
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
);
}
Router Methods
The router
object provides several navigation methods:
// Navigate to a specific URL
router.push('/dashboard');
// Replace the current URL (doesn't add to history)
router.replace('/dashboard');
// Go back to the previous page
router.back();
// Go forward
router.forward();
// Refresh the current page
router.reload();
Passing Query Parameters
You can pass query parameters programmatically:
router.push({
pathname: '/products',
query: { category: 'electronics', sort: 'price-asc' },
});
This navigates to /products?category=electronics&sort=price-asc
.
Handling Navigation Events
You can detect and respond to route changes:
import { useEffect } from 'react';
import { useRouter } from 'next/router';
function MyComponent() {
const router = useRouter();
useEffect(() => {
const handleRouteChange = (url) => {
console.log('Route changing to:', url);
// Analytics tracking or other side effects
};
router.events.on('routeChangeStart', handleRouteChange);
return () => {
router.events.off('routeChangeStart', handleRouteChange);
};
}, [router]);
// Component code...
}
Real-World Examples
E-commerce Product Navigation
import Link from 'next/link';
function ProductList({ products }) {
return (
<div className="product-grid">
{products.map((product) => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>${product.price.toFixed(2)}</p>
<Link href={`/products/${product.id}`} className="view-button">
View Details
</Link>
<button
className="add-to-cart"
onClick={() => addToCart(product)}
>
Add to Cart
</button>
</div>
))}
</div>
);
}
Navigation Guards with Programmatic Routing
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useAuth } from '../context/auth';
function ProtectedPage({ children }) {
const router = useRouter();
const { user, loading } = useAuth();
useEffect(() => {
if (!loading && !user) {
// Redirect to login if not authenticated
router.push({
pathname: '/login',
query: { returnUrl: router.asPath }, // Remember the current URL
});
}
}, [user, loading, router]);
if (loading || !user) {
return <div>Loading...</div>;
}
return <>{children}</>;
}
export default function Dashboard() {
return (
<ProtectedPage>
<h1>Dashboard</h1>
<p>Welcome to your dashboard!</p>
</ProtectedPage>
);
}
Multi-step Form with Navigation
import { useState } from 'react';
import { useRouter } from 'next/router';
function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({});
const router = useRouter();
const nextStep = () => {
setStep(step + 1);
// Update URL to reflect current step without page reload
router.push(`/form?step=${step + 1}`, undefined, { shallow: true });
};
const prevStep = () => {
setStep(step - 1);
router.push(`/form?step=${step - 1}`, undefined, { shallow: true });
};
const handleSubmit = async () => {
// Submit data
await submitFormData(formData);
router.push('/confirmation');
};
return (
<div>
<h2>Step {step} of 3</h2>
{step === 1 && (
<div>
<h3>Personal Information</h3>
{/* Form fields */}
<button onClick={nextStep}>Continue</button>
</div>
)}
{step === 2 && (
<div>
<h3>Address Details</h3>
{/* Form fields */}
<button onClick={prevStep}>Back</button>
<button onClick={nextStep}>Continue</button>
</div>
)}
{step === 3 && (
<div>
<h3>Confirmation</h3>
{/* Review information */}
<button onClick={prevStep}>Back</button>
<button onClick={handleSubmit}>Submit</button>
</div>
)}
</div>
);
}
Performance Considerations
Prefetching
Next.js automatically prefetches links that appear in the viewport by default. However, you can disable this behavior for certain links:
<Link href="/large-page" prefetch={false}>
View Large Page
</Link>
Shallow Routing
When you want to change the URL without running data fetching methods again (like getServerSideProps
), you can use shallow routing:
router.push('/dashboard?tab=profile', undefined, { shallow: true });
This is useful for tab interfaces or filtering where you want to update the URL but don't need to fetch new data.
Summary
Next.js provides powerful tools for navigation in your applications:
- The
Link
component enables client-side navigation between pages with automatic code-splitting and prefetching - Programmatic navigation through the
useRouter
hook gives you fine-grained control over routing - Router events allow you to hook into navigation lifecycle for analytics or custom behaviors
- Shallow routing helps optimize performance when updating the URL without changing page content
By leveraging these features, you can create fast, responsive applications with smooth navigation experiences for your users.
Exercises
-
Create a simple Next.js application with a home page, about page, and contact page. Implement navigation using the
Link
component. -
Build a dynamic product listing page that links to individual product detail pages using dynamic routes.
-
Implement an authentication system with protected routes that redirect unauthenticated users to a login page.
-
Create a tabbed interface that updates the URL when switching between tabs using shallow routing.
-
Build a pagination component that navigates between pages and preserves filter settings through query parameters.
Additional Resources
- Next.js Documentation: Routing
- Next.js Documentation: Link Component
- Next.js Documentation: Router
- Learn Next.js
Happy coding!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)