Creating a link with an active state in Next.js

There is something that, at the moment I write these lines, still lacks in Next.js: a component <Link /> showing a different class while the page is being visited.

Why use the link if you can use normal anchors?

Before continuing, a s…


This content originally appeared on DEV Community and was authored by Elves Sousa

There is something that, at the moment I write these lines, still lacks in Next.js: a component <Link /> showing a different class while the page is being visited.

Why use the link if you can use normal anchors?

Before continuing, a small pause, to see why using <Link /> instead of an <a>.

Basically, every time you use a normal anchor, the page makes a full refresh. The <Link /> component changes this behavior by loading only what changes on the screen, avoiding unnecessary rendering and making the experience faster and smoother. This is just for internal links; for the external ones, the anchor is enough.

React and Gatsby projects

In a React (CRA) project, this already comes by default with the React Router DOM library: just import a component <Link /> that comes with it, and add the activeClassName attribute, informing a CSS class for the active state of that anchor.

import { Link } from "react-router-dom"

export function Nav() {
  return (
    <nav>
      <Link to="/" activeClassName="active">
        Home
      </Link>
      <Link to="/blog" activeClassName="active">
        Blog
      </Link>
      <Link to="/about" activeClassName="active">
        About
      </Link>
    </nav>
  )
}

In Gatsby, another framework for creating static pages in React, the same can be achieved through the Gatsby library.

import { Link } from "gatsby"

export function Nav() {
  return (
    <nav>
      <Link to="/" activeClassName="active">
        Home
      </Link>
      <Link to="/blog" activeClassName="active">
        Blog
      </Link>
      <Link to="/about" activeClassName="active">
        About
      </Link>
    </nav>
  )
}

However, in Next.js, for some reason I don't know yet, the implementation of the <Link /> component is quite different: a child element is required and there are no to and activeClassName properties.

import Link from "next/link"

export function Nav() {
  return (
    <nav>
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/blog">
        <a>Blog</a>
      </Link>
      <Link href="/about">
        <a>About</a>
      </Link>
    </nav>
  )
}

It is a good implementation, meets multiple needs, but still lacks support for a class for the active state, as seen in previous examples.

How to bring activeClassName support to Next.js

Let's now create the <ActiveLink />: a component which will have the active class support. Here, the code is in typescript, but if your project uses JavaScript, the code works as well: just remove the typing. The component has only the required code for this feature to work.

First, we create the basic structure:

import { useRouter } from "next/router"
import Link from "next/link"

export function ActiveLink() {
  const { asPath } = useRouter()

  return <Link>...</Link>
}

The "hook" function useRouter is imported from Next.js, so that our component has information for the current route. This hook has the asPath property, which informs the current path of the page.

After this, let's create the properties of our component:

import { ReactElement } from "react"
import { useRouter } from "next/router"
import Link, { LinkProps } from "next/link"

type ActiveLinkProps = {
  children: ReactElement
  activeClassName: string
}

export function ActiveLink({ children, activeClassName }: ActiveLinkProps) {
  const { asPath } = useRouter()

  return <Link>{children}</Link>
}

Here, I use the ActiveLinkProps type to inform the properties that the component will accept:

  • children: It is a ReactElement type, that is, accepts a single React element as a parameter. If a ReactNode or JSX.Element type is used, it works as well, but as we will only have one element as child, is better to ReactElement.
  • activeClassName: With the 'string' type, as a simple text is enough to enter the name of a valid CSS class.

The problem is that at this time, the component doesn't have access to the properties of a normal <Link />. To do this, you need to extend the ActiveLinkProps type. Without these properties, the component will not work as a real replacement to the Next.js default link. Thus, it is necessary to import the Linkprops definition that comes with next/link:

import Link, { LinkProps } from "next/link"

After this, we make ActiveLinkProps aware of LinkProps type properties.

...

type ActiveLinkProps = LinkProps & {
  children: ReactElement
  activeClassName: string
}

...

Inside the component, an argument is then added to the function with the spread operator1, so that all the native properties of the Next.js link can be accessed and passed on to the returned component in the function.

import { ReactElement } from "react"
import { useRouter } from "next/router"
import Link, { LinkProps } from "next/link"

type ActiveLinkProps = LinkProps & {
  children: ReactElement
  activeClassName: string
}

export function ActiveLink({
  children,
  activeClassName,
  ...rest
}: ActiveLinkProps) {
  const { asPath } = useRouter()

  // The "...rest" represents all properties coming from LinkProps
  return <Link {...rest}>...</Link>
}

Now just make a conditional that verifies if the current route is the same as the "href" of the component.

const className = asPath === rest.href ? activeClassName : ""

If true, the class informed in activeClassName will be used.

Applying className in children components

Next.js' default implementation of <Link /> doesn't accept a className property. This should be passed on to a child element, otherwise it will not work:

<Link href="/">
  <a className="meuLink">Home</a>
</Link>

Therefore, to pass the property the correct way, we need to use the React.cloneElement()2 method to clone the child element, and passing className to it.

The final code will look like this:

import { cloneElement, ReactElement } from "react"
import { useRouter } from "next/router"
import Link, { LinkProps } from "next/link"

type ActiveLinkProps = LinkProps & {
  children: ReactElement
  activeClassName: string
}

export function ActiveLink({
  children,
  activeClassName,
  ...rest
}: ActiveLinkProps) {
  const { asPath } = useRouter()
  const className = asPath === rest.href ? activeClassName : ""

  return <Link {...rest}>{cloneElement(children, { className })}</Link>
}

That's all folks!

Links

If this article helped you in some way, consider donating. This will help me to create more content like this!

  1. Spread operator: Read more about it at MDN

  2. React.cloneElement: See more at React docs 


This content originally appeared on DEV Community and was authored by Elves Sousa


Print Share Comment Cite Upload Translate Updates
APA

Elves Sousa | Sciencx (2021-10-01T20:52:19+00:00) Creating a link with an active state in Next.js. Retrieved from https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/

MLA
" » Creating a link with an active state in Next.js." Elves Sousa | Sciencx - Friday October 1, 2021, https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/
HARVARD
Elves Sousa | Sciencx Friday October 1, 2021 » Creating a link with an active state in Next.js., viewed ,<https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/>
VANCOUVER
Elves Sousa | Sciencx - » Creating a link with an active state in Next.js. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/
CHICAGO
" » Creating a link with an active state in Next.js." Elves Sousa | Sciencx - Accessed . https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/
IEEE
" » Creating a link with an active state in Next.js." Elves Sousa | Sciencx [Online]. Available: https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/. [Accessed: ]
rf:citation
» Creating a link with an active state in Next.js | Elves Sousa | Sciencx | https://www.scien.cx/2021/10/01/creating-a-link-with-an-active-state-in-next-js/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.