How to show a top progress bar when route changes on Next.js 15?

3 min read
Cover Image for How to show a top progress bar when route changes on Next.js 15?

Have you ever noticed the thin red progress bar at the top of YouTube when you navigate between pages? Many popular single-page applications (SPAs) use a top progress bar to indicate page transitions, improving the user experience (UX). Platforms like GitHub, YouTube, and many others use this feature to give users a sense of loading feedback when switching between routes.

In this article, we’ll learn how to implement a similar top progress bar in Next.js 15.

1) Create a Next.js project

npx create-next-app@latest

On installation, you'll see the following prompts:

What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like your code inside a `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to use Turbopack? (recommended) No / Yes
Would you like to customize the import alias (`@/*` by default)? No / Yes
What import alias would you like configured? @/*

After the prompts, create-next-app will create a folder with your project name and install the required dependencies.

2) Create Loader component

In components directory, create a Loader.tsx (or jsx if you are using JavaScript) file and copy and paste the code below:

"use client"; //makes the component client so we can use hooks 

import React, { useEffect, useState } from "react";
import { usePathname } from "next/navigation";

const Loader = () => {
  const pathname = usePathname();
  const [progress, setProgress] = useState(0);
  const [hidden, setHidden] = useState(true);

  useEffect(() => {
    setProgress(60);
    setHidden(false);

    const timer = setTimeout(() => {
      setProgress(100);

      setTimeout(() => {
        setHidden(true);
        setProgress(0);
      }, 200);
    }, 500);

    return () => clearTimeout(timer);
  }, [pathname]);

  return (
    <div
      className={`h-[2px] bg-goldDark fixed top-0 left-0 transition-all duration-300 ${
        hidden ? "opacity-0 w-0" : "opacity-100 w-full z-[70]"
      }`}
      style={{
        width: `${progress}%`,
        transition: progress === 100 ? "none" : "width 0.3s ease-in-out",
      }}
    />
  );
};

export default Loader;

If you aren’t using Tailwind CSS, style the component on your own.

3) Render this component in layout.tsx (or layout.js)

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Header from "@/components/Header";
import Loader from "@/components/Loader"; //import the component

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Sarvarbek | Welcome!",
  description: "Ilyosov Sarvarbek, web and mobile app developer. (Personal Blog)",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background min-h-screen`}
      >
        <Loader/> {/*Render it before everything*/}
        <Header/>
        {children}
      </body>
    </html>
  );
}

And that’s it! 🎉 Now, your Next.js app has a YouTube-like top progress bar when navigating between pages. This small but useful feature improves the user experience by providing a smooth transition effect when loading new pages.

If you found this tutorial helpful, consider sharing it with others or leaving a comment. Happy coding! 🚀

© 2025 Sarvarbek's Blog