Auto Navbar

Used in this guide:
Next.js 15.4.2

Version 1: Basic

app/Navbar.tsx
import path from "path";
import fs from "fs";
import Link from "next/link";
 
const Navbar = () => {
 
    const pagesDir = path.join(process.cwd(), "app/(pages)");
    const entries = fs.readdirSync(pagesDir, { withFileTypes: true });
    const folderNames = ["", ...entries
        .filter(folder => folder.isDirectory())
        .map((item) => item.name)];
 
    return (
        <div className="py-8 flex gap-2">
            {folderNames.map((item) => (
                <Link key={item} href={`/${item}`}>
                    <button
                        className="py-2 px-4 text-sm bg-sky-700 hover:bg-sky-500 cursor-pointer"
                        >{item === "" ? "Home" : item}</button>
                </Link>
            ))}
        </div>
    )
}
 
export default Navbar;


Version 2: With Auto-Highlight Current Page

app/components/navbar/Navbar.tsx
'use client'
import Link from "next/link";
import { usePathname } from "next/navigation";
 
interface NavbarProps {
    folderNames: string[];
}
 
const Navbar = ({ folderNames }: NavbarProps) => {
 
    const pathname = usePathname();
    console.log(pathname);
 
    return (
        <div className="flex gap-2 mb-6">
            {folderNames.map((item) => {
                const href = item === "" ? "/" : `/${item}`;
                const isActive = pathname === href;
                const buttonClasses = `py-2 px-4 cursor-pointer text-sm capitalize ${isActive ? "bg-sky-500" : "bg-sky-700 hover:bg-sky-500"}`;
 
                return (
                    <Link key={item} href={href}>
                        <div className={buttonClasses}>
                            {item === "" ? "Home" : item}
                        </div>
                    </Link>
                )
            })}
        </div>
    )
}
 
export default Navbar;
app/components/navbar/NavWrapper.tsx
import path from "path";
import fs from "fs"
import Navbar from "./Navbar";
 
const NavWrapper = () => {
 
    const pagesDir = path.join(process.cwd(), "app/(pages)");
    const entries = fs.readdirSync(pagesDir, { withFileTypes: true });
    const folderNames = ["", ...entries
        .filter(entry => entry.isDirectory())
        .map((e) => e.name)
    ];
 
    return (
        <Navbar folderNames={folderNames} />
    )
}
 
export default NavWrapper;
app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import NavWrapper from "./components/navbar/NavWrapper";
 
const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});
 
const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});
 
export const metadata: Metadata = {
  title: "Test - UpCloud Server",
  description: "Generated by create next app",
};
 
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased
          flex flex-col w-full max-w-[1280px] mx-auto p-6`}
      >
        <NavWrapper />
        {children}
      </body>
    </html>
  );
}

JKT

Stay focused, and the rest will follow

©Jakkrit Turner. All rights reserved