authenticationnext.js14clerk

Visiting home route (/) causing full page refresh in next.js 14 when using clerk.js


I am using @clerk/nextjs": "^4.29.5" for the authentication system for my next.js 14 app. I am managing some global states using context-api. My problem is when I revisit my home route (/) the page is fully reloading instead of soft navigation (other routes are ok). This only happens in the hosted (vercel) app, not in the localhost or even when I run the build version using npm start. How to fix this issue? What I'm doing wrong here? I don't want to reload my whole page.

Project structure:

app/
  (auth)
    layout.tsx
    sign-in/
    sign-up/
  (root)
    layout.tsx
    page.tsx
  layout.tsx

layout.ts:

import { MainContextProvider } from "@/contexts/MainContext";
import ClerkThemeProvider from "@/lib/providers/ClerkThemeProvider";
import { Suspense } from "react";
import MainPageFallback from "@/components/shared/MainPageFallback";
import { Toaster } from "@/components/ui/toaster";


export default function RootLayout({ children }: { children: React.ReactNode }) {
    return (
        <html lang="en">
            <body className={`${inter.className}`}>
                <MainContextProvider>
                    <Suspense fallback={<MainPageFallback />}>
                        <ClerkThemeProvider>{children}</ClerkThemeProvider>
                    </Suspense>
                </MainContextProvider>
                <Toaster />
            </body>
        </html>
    );
}

/contexts/MainContext.ts:


"use client";

import { createContext, useEffect, useState } from "react";

export const MainContext = createContext({
    lang: "",
    theme: "",
    sidebarOpen: false,
    handleLang: () => {},
    changeTheme: () => {},
    closeSidebar: () => {},
    openSidebar: () => {},
});

export const MainContextProvider = ({ children }: { children: React.ReactNode }) => {
    const [lang, setLang] = useState("en");
    const [theme, setTheme] = useState("light");
    const [sidebarOpen, setSidebarOpen] = useState(false);

    useEffect(() => {
        const savedTheme = localStorage.getItem("theme");
        const savedLang = localStorage.getItem("lang");

        setLang(savedLang ? savedLang : "en");
        setTheme(savedTheme ? savedTheme : "light");
    }, []);

    useEffect(() => {
        localStorage.setItem("lang", lang);
        document.documentElement.lang = lang;
    }, [lang]);

    useEffect(() => {
        localStorage.setItem("theme", theme);
        if (theme === "dark") {
            document.body.classList.add("dark");
        } else {
            document.body.classList.remove("dark");
        }
    }, [theme]);

    const handleLang = () => {
        setLang(lang === "en" ? "bn" : "en");
    };

    const changeTheme = () => {
        setTheme(theme === "light" ? "dark" : "light");
    };

    const openSidebar = () => {
        setSidebarOpen(true);
    };

    const closeSidebar = () => {
        setSidebarOpen(false);
    };

    return (
        <MainContext.Provider
            value={{
                lang,
                theme,
                sidebarOpen,
                handleLang,
                openSidebar,
                changeTheme,
                closeSidebar,
            }}
        >
            {children}
        </MainContext.Provider>
    );
};

/lib/providers/ClerkThemeProvider.tsx


"use client";

import { MainContext } from "@/contexts/MainContext";
import { ClerkProvider } from "@clerk/nextjs";
import { dark } from "@clerk/themes";
import { useContext } from "react";

export default function ClerkThemeProvider({ children }: { children: React.ReactNode }) {
    const { theme } = useContext(MainContext);

    return (
        <ClerkProvider appearance={{ baseTheme: theme === "dark" ? dark : undefined }}>
            {children}
        </ClerkProvider>
    );
}

middleware.ts


import { authMiddleware } from "@clerk/nextjs";

export default authMiddleware({
    // An array of public routes that don't require authentication.
    publicRoutes: ["/", "/about", "/contact", "/api/webhooks(.*)", "/api/uploadthing"],

    // An array of routes to be ignored by the authentication middleware.
    ignoredRoutes: ["/api/webhooks(.*)"],
});

export const config = {
    matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};


Solution

  • I solved the issue. I want to share it. It's a straightforward cause, why the home route always fully reloads on the deployed version. It's none of the clerk package cause.

    I just had to add a loading.tsx file on the app/ folder and then the full reload will not occur anymore.