Skip to content
FLAVIO COPES
flaviocopes.com
2026

Linking two pages in Next.js using Link

By Flavio Copes

Learn how to link two pages in Next.js with the Link component, how the pages folder maps to URLs, and how automatic code splitting keeps bundles small.

~~~

This tutorial starts where the first Next.js tutorial left off. We built a site with one page:

Browser showing localhost:3000 with Home page heading displayed on a white background

I want to add a second page to this website, a blog posts list. It’s going to be served into /blog, and for the time being it will just contain a simple static page, just like our first index.js component:

VS Code editor showing blog.js file with React component code including Blog heading in JSX

After saving the new file, the npm run dev process already running is already capable of rendering the page, without the need to restart it.

When we hit the URL http://localhost:3000/blog we have the new page:

Browser showing localhost:3000/blog with Blog heading displayed on a white background

and here’s what the terminal told us:

Terminal output showing Next.js compilation messages for /blog page build and ready status

Now the fact that the URL is /blog depends on just the filename, and its position under the pages folder.

You can create a pages/hey/ho page, and that page will show up on the URL http://localhost:3000/hey/ho.

What does not matter, for the URL purposes, is the component name inside the file.

Try going and viewing the source of the page, when loaded from the server it will list /_next/static/development/pages/blog.js as one of the bundles loaded, and not /_next/static/development/pages/index.js like in the home page. This is because thanks to automatic code splitting we don’t need the bundle that serves the home page. Just the bundle that serves the blog page.

Browser view source showing HTML with Next.js script tags and bundle references for blog page

We can also just export an anonymous function from blog.js:

export default () => (
  <div>
    <h1>Blog</h1>
  </div>
)

or if you prefer the non-arrow function syntax:

export default function() {
  return (
    <div>
      <h1>Blog</h1>
    </div>
  )
}

Now that we have 2 pages, defined by index.js and blog.js, we can introduce links.

Normal HTML links within pages are done using the a tag:

<a href="/blog">Blog</a>

We can’t do do that in Next.js.

Why? We technically can, of course, because this is the Web and on the Web things never break (that’s why we can still use the <marquee> tag. But one of the main benefits of using Next is that once a page is loaded, transitions to other page are very fast thanks to client-side rendering.

If you use a plain a link:

const Index = () => (
  <div>
    <h1>Home page</h1>
    <a href='/blog'>Blog</a>
  </div>
)

export default Index

Now open the DevTools, and the Network panel in particular. The first time we load http://localhost:3000/ we get all the page bundles loaded:

Chrome DevTools Network panel showing multiple JavaScript bundle requests when loading home page

Now if you click the “Preserve log” button (to avoid clearing the Network panel), and click the “Blog” link, this is what happens:

Chrome DevTools Network panel showing additional JavaScript requests after clicking Blog link with plain anchor tag

We got all that JavaScript from the server, again! But.. we don’t need all that JavaScript if we already got it. We’d just need the blog.js page bundle, the only one that’s new to the page.

To fix this problem, we use a component provided by Next, called Link.

We import it:

import Link from 'next/link'

and then we use it to wrap our link, like this:

import Link from 'next/link'

const Index = () => (
  <div>
    <h1>Home page</h1>
    <Link href='/blog'>
      <a>Blog</a>
    </Link>
  </div>
)

export default Index

Now if you retry the thing we did previously, you’ll be able to see that only the blog.js bundle is loaded when we move to the blog page:

Chrome DevTools Network panel showing only blog.js bundle loaded when using Next.js Link component for navigation

and the page loaded so faster than before, the browser usual spinner on the tab didn’t even appear. Yet the URL changed, as you can see. This is working seamlessly with the browser History API.

This is client-side rendering in action.

What if you now press the back button? Nothing is being loaded, because the browser still has the old index.js bundle in place, ready to load the /index route. It’s all automatic!

This is not the only way to handle linking in Next.js, but I think it’s the simplest one.

Tagged: Next.js · All topics
~~~

Related posts about next: