Ver en Español
Routing between pages in Next.js
Apr 12, 2023
Updated: Jun 25, 2026

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 used next/router and 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 from next/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 Link component 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 as prop is no longer used: just build the final URL and pass it to href.
  • The useRouter hook (from next/navigation) and its push method let us do programmatic routing, changing routes directly from our JavaScript code.

Exercises to practice

  1. Build a basic Next.js 16 app with at least three different pages (for example, Home, About, and Contact) and implement navigation using the Link component.
  2. 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 to href (without the as prop).
  3. Implement programmatic routing with useRouter from next/navigation and its push method. For example, build a search form and navigate to a results page based on the user's input.

3-point summary

  1. Next.js's Link component makes client side navigation easy; in Next.js 16 it renders the <a> for you, so you no longer include it manually.
  2. Dynamic routes are handled by passing the final URL directly to href; the old as prop is obsolete.
  3. Programmatic routing is achieved with the useRouter hook from next/navigation and its push method, 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

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias