advaiyalad
advaiyalad

Reputation: 163

Link not active in next.js even when I am on the page

I am trying to get a link to have the active class when clicked in next.js and react-bootstrap. Upon page load, the currently active route's link is not given the active class. When I click on the link after page load, then it is given the active class. I suspect that this has something to do with server-side rendering. Here is my code:

const Navigation = () => (
    <Navbar bg="warning" variant="light" collapseOnSelect expand="lg">
        <Link passHref href="/">
            <Navbar.Brand>...</Navbar.Brand>
        </Link>
        <Navbar.Toggle aria-controls="responsive-navbar-nav" />
        <Navbar.Collapse id="responsive-navbar-nav">
            <Nav className="ml-auto">
                <Link href="/">
                    <Nav.Link href="/" as="a">
                        Home
                    </Nav.Link>
                </Link>
            </Nav>
        </Navbar.Collapse>
    </Navbar>
);

export default Navigation;

Upon page load: link upon page load After clicking link manually: link after clicking manually

Is there a way to make the link highlighted on the server (without a hacky solution)?

Upvotes: 0

Views: 3001

Answers (1)

Darryl RN
Darryl RN

Reputation: 8238

As I know, so far there is no built-in feature like that whether in NextJS or react-bootstrap.

The example below is not a hacky solution, it is the best practice introduced by NextJS.

You need to use a class to show an active link, then have this function to check if the current url is same as the url in next/router:

import { useRouter } from 'next/router'
import PropTypes from 'prop-types'
import Link from 'next/link'
import React, { Children } from 'react'

const ActiveLink = ({ children, ...props }) => {
  const { asPath } = useRouter()
  const child = Children.only(children)
  const childClassName = child.props.className || ''

  // pages/index.js will be matched via props.href
  // pages/about.js will be matched via props.href
  // pages/[slug].js will be matched via props.as
  const className =
    asPath === props.href || asPath === props.as
      ? `${childClassName} active`.trim()
      : childClassName

  return (
    <Link {...props}>
      {React.cloneElement(child, {
        className: className || null,
      })}
    </Link>
  )
}

ActiveLink.propTypes = {
  activeClassName: PropTypes.string.isRequired,
}

export default ActiveLink

Then in your component:

const Navigation = () => (
    <Navbar bg="warning" variant="light" collapseOnSelect expand="lg">
        <Link passHref href="/">
            <Navbar.Brand>...</Navbar.Brand>
        </Link>
        <Navbar.Toggle aria-controls="responsive-navbar-nav" />
        <Navbar.Collapse id="responsive-navbar-nav">
            <Nav className="ml-auto">
                <Link href="/">
                    <ActiveLink href="/">
                        <Nav.Link href="/" as="a">
                            Home
                        </Nav.Link>
                    </ActiveLink>
                </Link>
            </Nav>
        </Navbar.Collapse>
    </Navbar>
);

export default Navigation;

source

Upvotes: 3

Related Questions