Skip to content

Hiding Components on Page Change in Next.js

Next.js/

It’s a common behavior to close a navigation menu, sidebar or pop-up while navigating to a new page.

But, it’s also a common problem in Next.js to see the menu remain open after navigating.

So, how do you hide a component while navigating between pages?

The answer is to use React’s useEffect hook. Let me show you how.

Hiding a Component After Route Change

Here’s a a basic example of a menu component that remains open after page change.

import { useState } from 'react';

export default function MobileMenu() {
  const [isOpen, setIsOpen] = useState(false);

  function setIsOpenValue(value) {
    setIsOpen(value);
  }

  return (
    <>
      <button onClick={() => setIsOpenValue(true)}>Open menu</button>
      <nav className={`${isOpen ? 'open' : 'close'}`}>
        <button onClick={() => setIsOpenValue(false)}>
          Close menu
        </button>
        <Navigation />
      </nav>
    </>
  );
}

It uses isOpen state to determine if the menu should be visible or not. When the menu is open, it renders a Navigation component that contains links to other pages. But, clicking a link and opening another page keeps the menu.

To fix this problem, you need to change your component’s state when the route has changed.

You can do so by using useEffect hook and adding asPath property from useRouter as a dependency.

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

export default function MobileMenu() {
  const [isOpen, setIsOpen] = useState(false);
  const { asPath } = useRouter();

  useEffect(() => {
    setIsOpen(false);
  }, [asPath]);

  // ✂️
}

Why use asPath and not pathname instead? It is better to use asPath over pathname, because pathname doesn’t change while navigating between dynamic routes.

If you go from /categories/react/ to /categories/nextjs/, the useEffect hook won’t fire, because pathname remains the same —/categories/[name]. But, asPath always reflects the path the way you would see in your browser.

Hiding Components Using Router Events

Alternatively, you can register router events inside useEffect to achieve the same result.

You need to create a handler function that will hide your component, and register it on routeChangeComplete event.

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

export default function MobileMenu() {
  const [isOpen, setIsOpen] = useState(false);
  const router = useRouter();

  useEffect(() => {
    const handleRouteChange = () => {
      setIsOpen(false);
    };

    router.events.on('routeChangeComplete', handleRouteChange);

    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router]);

  // ✂️
}

Your useEffect hook should return a function that removes your handler function from the routeChangeComplete event.

If you want your component to disappear immediately when navigation begins, you can use routeChangeStart event instead.

- router.events.on('routeChangeComplete', handleRouteChange);
+ router.events.on('routeChangeStart', handleRouteChange);

return () => {
- router.events.off('routeChangeComplete', handleRouteChange);
+ router.events.off('routeChangeStart', handleRouteChange);
};

This will close the menu after clicking a link, even before the navigation has finished.