I have created a static website using Next.js and Tailwind CSS with several pages, including Home, Services, and Contact. Each route correctly navigates to its respective page. Everything works fine on localhost, but when I deploy the build to Firebase Hosting, the routes work only if I don't refresh the page. If I refresh the page, the site defaults to the Home page and I can't navigate to other pages. For example, if I navigate to the Services page and then refresh the page, it shows the Home page while the URL still indicates the Services page.
Here is my code -
import { memo, useEffect, useState } from 'react';
import Link from "next/link";
import Banner from "./Banner";
import Button from './Buttons';
import Image from "next/image";
import { AppRoutes } from '@/constants/appRoutes';
import BrandLogo from '@/assets/yume-logo.svg'
import BrandLogoText from '@/assets/yume-logo-text.svg'
import { usePathname } from 'next/navigation';
const Header = () =>{
// Get the current pathname from the router.
const router = usePathname()
// State to manage the active navigation item and menu open/close status.
const [activeNav, setActiveNav] = useState('');
const [menuOpen, setMenuOpen] = useState(false);
// Update the activeNav state when the pathname changes.
useEffect(() => {
setActiveNav(router);
}, [router]);
// Toggle the menu open/close status.
const toggleMenu = () => {
setMenuOpen(!menuOpen);
};
// Close the menu when a navigation item is clicked.
const closeMenu = () => {
setMenuOpen(false);
};
return(
<>
<Banner/>
<header className="sticky top-0 w-full z-40 md:py-1 py-0 bg-white drop-shadow-sm">
<nav className='container md:max-w-screen-xl mx-auto flex flex-col md:justify-between md:flex-row md:px-0'>
<div className='flex justify-between w-full px-8 py-4'>
<Link
className='flex gap-4 items-center max-w-fit'
onClick={closeMenu}
href={AppRoutes.HOME_PAGE}>
<Image
className='h-7 max-w-fit'
src={BrandLogo}
alt="yume labs logo" />
<Image
className='h-3 max-w-fit'
src={BrandLogoText}
alt="yume labs logo" />
</Link>
<div className="w-8 h-8 flex flex-col align-middle justify-center gap-1 md:hidden" onClick={toggleMenu} aria-hidden>
<div className='w-full h-1 bg-gray-600'></div>
<div className='w-full h-1 bg-gray-600'></div>
<div className='w-full h-1 bg-gray-600'></div>
</div>
</div>
<div className='flex md:flex-row flex-col md:items-center gap-4 md:w-fit py-8 md:py-0'>
<div className='px-8 py-4 flex'>
<Link
className='w-full h-full whitespace-nowrap'
href={AppRoutes.SERVICES}>
Our Services
</Link>
</div>
<div className='px-8 md:px-0'>
<Link
href={AppRoutes.CONTACT_US}>
<Button
label='Contact Us'
btnsize='sm'
type='button'
/>
</Link>
</div>
</div>
</nav>
</header>
</>
)
}
export default memo(Header)
I have finally found the answer. It is the problem related with firebase and how it handles client side routing for single-page applications.
Basically, firebase hosting serves the index.html file whenever a request does not match a file in the public directory, which results in the app being unable to handle the routing correctly upon refresh.
I had to add cleanURLs in firebase Json
{
"hosting": {
"public": "out",
"cleanUrls": true,
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}