Nov 1, 2025

How to make Next.js navigation faster

Atik

After App Router dropped in Next.js pages feels genuinely faster for fully server rendered pages. But one thing that I don't quite like is page navigation. User always has to wait for the server call to finish.

App router navigation feels slow. For dashboards, this is quite unacceptable. Route doesn't change instantly when user clicks on something.

To bring back instant transitions without giving up SSR entirely, we use a hybrid setup


React Router!


Install React Router

npm i react-router


Create a App Shell

"use client";

// Load Next.js helper for on-demand imports.
import dynamic from "next/dynamic";

// Load RouterApp only in the browser (turn off server-side rendering).
const RouterApp = dynamic(() => import("@/components/router/router-app"), {
  ssr: false,
});

// Page component that shows the app shell.
const SpaPage = () => {
  return <RouterApp />;
};

export default SpaPage;


Routes with React Router

# React Routes

import { BrowserRouter, Routes, Route, useParams } from "react-router-dom";

const Home = () => <div>Home</div>;
const About = () => <div>About</div>;

// Dynamic page reading a URL param
const UserProfile = () => {
  const { userId } = useParams();
  return <div>User Profile for ID: {userId}</div>;
};

// Router with a dynamic route
const RouterApp = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />            {/* Landing */}
        <Route path="/about" element={<About />} />      {/* Static page */}
        <Route path="/users/:userId" element={<UserProfile />} /> {/* Dynamic */}
      </Routes>
    </BrowserRouter>
  );
};

export default RouterApp;


Update Next.js config

module.exports = {
  async rewrites() {
    return [
      {
        // Match any URL path starting with "/" that is NOT:
        // - "/api"              (API routes)
        // - "/_next" or "/_static" (Next.js/internal assets)
        // - "/favicon.ico"      (the favicon)
        // - any path containing a dot (e.g., "/file.js", "/images/logo.png")
        source: "/((?!api|_next|_static|favicon.ico|.*\\..*).*)",
        destination: "/dashboard/spa",
      },
    ];
  },
};


This gives us the best of both worlds, server-rendered entry and SPA-like snappiness once inside.

Get technical deep dives and updates like this, right in your inbox.

Get technical deep dives and updates like this, right in your inbox.

Get technical deep dives and updates like this, right in your inbox.