Sebastian Gomez
Routing between pages in Next.js
Next.js is a powerful framework that helps us build fast, efficient web apps. In this article we will explore some very useful features that let us navigate and optimize navigation within our applications.
Updated for Next.js 16 and React 19. The concepts in this post (client side navigation, dynamic routes, and programmatic routing) are still fully valid, but the API changed quite a bit since 2022. All the code here is written for the App Router, which is today's recommended approach. Further down I explain the difference with the old Pages Router in case you maintain a legacy project.
Pages Router versus App Router
Before we start, let's get our bearings. Next.js today has two routing systems:
- Pages Router (
pages/): the classic model, prior to Next.js 13. It usednext/routerand a<Link>syntax that is now obsolete. It is still supported, but it is no longer the recommendation for new projects. - App Router (
app/): stable since Next.js 13.4 and the default since Next.js 14. It is built on React Server Components, folders as routes, special files (page.jsx,layout.jsx), and the hooks fromnext/navigation.
In this article we will use the App Router, since that is what you will find in any freshly created Next.js 16 project.
The Link component
The Link component is similar to HTML's <a> tag and lives in the next/link module. It lets us do client side routing, meaning we navigate between pages without reloading the whole site.
Example:
<Link href="/settings">settings</Link>Important change from older tutorials: You used to have to wrap an <a> inside Link (<Link href="/settings"><a>settings</a></Link>). Since Next.js 13 that pattern became obsolete, and in Next.js 16 it was removed entirely: Link now renders the <a> for you. Just put the content directly inside Link.
For dynamic routes you no longer need the old as prop. Just build the final URL and pass it directly to href:
<Link href={`/user/${user.id}`}>user</Link>Building a notes app
Let's see how we can link our pages in a notes app using the Link component. With the App Router, each route is a folder with a page.jsx file.
app/notes/[id]/page.jsx
// This directive marks the file as a Client Component so we can use hooks
'use client'
// Import the 'useParams' hook from 'next/navigation' to read the route params
import { useParams } from 'next/navigation'
// Import the 'Link' component from 'next/link' to navigate between pages
import Link from 'next/link'
// Named default-exported component (better than an anonymous function)
export default function NotePage() {
// 'useParams' gives us the dynamic route params; here we pull out 'id'
const { id } = useParams()
return (
<div>
{/* Heading that shows the 'id' value taken from the route */}
<h1>Note: {id}</h1>
{/* 'Link' to navigate to the 'notes' page without reloading the app */}
<Link href="/notes">Notes</Link>
</div>
)
}Note: useParams is a client hook, which is why the file carries the 'use client' directive on its first line. If you prefer a Server Component, read the id from the params prop that Next.js passes to the page. in Next.js 15 and 16 params may be delivered as a promise in Server Components (const { id } = await params); confirm the exact shape for your minor version before publishing.
app/notes/page.jsx
// Import the 'Link' component from 'next/link' to navigate between pages
import Link from 'next/link'
// Named default-exported component
export default function NotesPage() {
// Build an array of 15 'note' objects with 'id' and 'title' properties
const notes = new Array(15).fill(1).map((e, i) => ({ id: i, title: `Note: ${i}` }))
return (
<div>
{/* Page title */}
<h1>Notes</h1>
{/* Loop over the notes array and render one link per note */}
{notes.map((note) => (
// Container for the individual note; the 'key' goes on the outermost element
<div key={note.id}>
{/* We pass the final URL directly to 'href': the 'as' prop is no longer used */}
<Link href={`/notes/${note.id}`}>
<strong>{note.title}</strong>
</Link>
</div>
))}
</div>
)
}app/page.jsx
import Link from 'next/link'
export default function IndexPage() {
return (
<div>
<h1>Index page</h1>
<Link href="/notes">Notes</Link>
</div>
)
}Changing routes from JavaScript
When we need to change routes from our JavaScript code instead of using the Link component, we can use the router object and its push method to do programmatic routing. In the App Router, the useRouter hook is imported from next/navigation and only works inside Client Components.
Example:
// This directive marks the file as a Client Component so we can use hooks and events
'use client'
// Import the 'useRouter' hook from 'next/navigation' (not from 'next/router')
import { useRouter } from 'next/navigation'
// Named default-exported component
export default function NavigationButtons() {
// 'useRouter' gives us an instance of the Next.js router
const router = useRouter()
// Define the constant 'id' with a value of 2
const id = 2
return (
<div>
{/* Button that navigates to the home page '/' */}
<button onClick={() => router.push('/')}>
Go Home
</button>
{/* Button that navigates to '/user/<id>': we pass the fully resolved URL */}
<button onClick={() => router.push(`/user/${id}`)}>
Dashboard
</button>
</div>
)
}Important change: In the old Pages Router, router.push took a second as argument (router.push('/user/[id]', '/user/' + id)). In the App Router that is gone: you pass the final URL directly as the single argument.
Conclusions
- The
Linkcomponent lets us do client side routing in our Next.js apps, with an approach similar to HTML's<a>tag, but you no longer wrap an<a>inside it. - To link dynamic routes, the
asprop is no longer used: just build the final URL and pass it tohref. - The
useRouterhook (fromnext/navigation) and itspushmethod let us do programmatic routing, changing routes directly from our JavaScript code.
Exercises to practice
- Build a basic Next.js 16 app with at least three different pages (for example, Home, About, and Contact) and implement navigation using the
Linkcomponent. - Add a dynamic route to your app, such as
app/products/[id]/page.jsx, and create links to different products by passing the final URL tohref(without theasprop). - Implement programmatic routing with
useRouterfromnext/navigationand itspushmethod. For example, build a search form and navigate to a results page based on the user's input.
3-point summary
- Next.js's
Linkcomponent makes client side navigation easy; in Next.js 16 it renders the<a>for you, so you no longer include it manually. - Dynamic routes are handled by passing the final URL directly to
href; the oldasprop is obsolete. - Programmatic routing is achieved with the
useRouterhook fromnext/navigationand itspushmethod, giving you more flexibility when changing routes from your JavaScript code.
That's all, I hope this post is useful to you and that you can apply it to a project you have in mind. If you have any questions, opinions, or want to add something, feel free to leave a comment below. And if you liked it, don't forget to share it on your social networks. Good luck!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.