Go back

Fixing href interpolation errors in Next.js

Jul 12, 2023 · 5 min read

For one, I honestly wouldn't think I'd ever come across this type of error below.

href interpolation error

But, as I read through so many issues and discussions on GitHub, I began to understand why I ran into this error.

Normally, if you want to navigate to another page in Next.js, you'd need to use the link component like so:

<Link href="/path-to/where">Some text</Link>

And that's it. You won't need to worry about breaking what's not broken.


But, the situation was different for me.

Some days back I shared an opinion around keeping component state in the browser URL, and I have, before then, used this paradigm in most of my projects.

Ideally, I kept the state of a particular component in the URL, when it was time to navigate from the page where that component is currently in use — havoc wreaked! And the aforementioned error was thrown to my face!

I wept, and cussed as if it'd take away the despair that the error brought, Lol!

Anyway, in the error modal where the information about the error lies, was a link to a page on how to fix this interpolation error. Unfortunately, it did not work as it should.

Take a look at a similar snippet they recommended using.

Lest I forget, this error is predominant when you're working with dynamic routes in Next.js

import Link from 'next/link'
 
export default function SomePage() {
  return (
    <>
      <Link
        href={{
          pathname: '/page/dynamic-segment/',
          query: { slug: 'the dynamic segment' },
        }}
      >
        Go to dynamic page
      </Link>
    </>
  )
}

So, if you happen to keep state in the URL with query parameters, You'll modify the snippet above to look like the one below

import Link from 'next/link'
 
export default function SomePage() {
  return (
    <>
      <Link
        href={{
          pathname: '/page/dynamic-segment?item=bag',
          query: { slug: 'the dynamic segment', item: item },
        }}
      >
        Go to dynamic page
      </Link>
    </>
  )
}

Sometimes writing out the exact path can be difficult. Instead, you should append the pathname from next/router like so:

<Link
  href={{
    pathname: router.pathname,
    query: { slug: 'the dynamic segment', item: item },
  }}
>
  Go to dynamic page
</Link>

But, in all this, I did not use the <Link> component. I used the push() and replace() methods of next/router interchangeably because I thought that the Link component could be buggy! Naive me!

So, the same action with the link component to navigate to another page became this, for me.

const { query } = useRouter()
const item = query
 
const handleNavigate = () => {
  router.replace({
    pathname: `/page/${dynamic_segment}?item=bag`,
    query: { item: item },
  })
}

Finally, a fix

I found solace in the fact that a lot of people shared this same grievance, after going through a lot of discussions and issues in the Next.js repo.

But, for how long? How long will I keep scouring through GitHub issues, discussions, and stackoverflow to no avail?

One particular stackoverflow answer stood out and gave me a clue as to how I could fix this. Adding a unique key that accepts the global router path as a value in _app.js supposedly fixed the error.

import type { AppProps } from 'next/app'
import Head from 'next/head'
import React from 'react'
import { useRouter } from 'next/router'
 
export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter()
 
  return (
    <>
      <Head>
        <title>Page Title</title>
      </Head>
      <Component {...pageProps} key={router.asPath} />
    </>
  )
}

But, why did this work?

Recall how we've always been told to add a key prop to an element whenever we're performing a map() operation on an array to let each item become a unique one.

The same approach applies here and here's why it worked:

When I tried navigating to a new dynamic route using the Link component, Next.js recognized that the component instance was the same and tried to update its props.

This is wrong, it should not happen like that. So why did it behave that way? It did because the Link component was not receiving any props that could uniquely identify it as a separate instance.

Since the component didn't have any distinct identification via a key like we usually do to list items, Next.js might have not triggered a re-render of the component or properly updated the href interpolation.


By adding the key prop to the App component and setting it to router.asPath, you're providing a unique identifier for each instance of the component.

router.asPath represents the current URL path, which makes sure that when you navigate to a new page, a new instance of the App component is created.

This unique identifier — key — helps Next.js recognize that a new instance of the App component is being rendered, and as a result, the Link component and its href interpolation are correctly initialized, avoiding the previous error.

Wrapping up

Since the Link component uses the underlying principles of next/router, even if you do not perform navigation with the Link component, instead you decided to focus on router.push or .replace() this solution still applies to both approaches.