0
0
NextJSframework~15 mins

Programmatic navigation (useRouter) in NextJS - Deep Dive

Choose your learning style9 modes available
Overview - Programmatic navigation (useRouter)
What is it?
Programmatic navigation in Next.js means changing pages using code instead of links you click. The useRouter hook gives you tools to move between pages inside your app by telling Next.js where to go next. This lets your app decide when and where to navigate based on user actions or events. It works behind the scenes to update the page without reloading the whole website.
Why it matters
Without programmatic navigation, users can only move between pages by clicking links or buttons with fixed destinations. This limits how interactive and dynamic your app can be. Programmatic navigation lets your app respond instantly to user choices, like after submitting a form or completing a task, improving user experience and flow. It makes your app feel smooth and smart, like a conversation rather than a static book.
Where it fits
Before learning useRouter, you should understand basic Next.js pages and how links work with the Link component. After mastering programmatic navigation, you can explore advanced routing features like dynamic routes, middleware, and server actions. This fits into the journey of building interactive, user-friendly web apps with Next.js.
Mental Model
Core Idea
useRouter is like a remote control that lets your app change pages by pressing buttons in code instead of clicking links.
Think of it like...
Imagine a TV remote control: instead of walking to the TV and pressing buttons on it, you use the remote to switch channels or adjust volume from your seat. useRouter is that remote for your app’s pages.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ User triggers │──────▶│ useRouter API │──────▶│ Page changes  │
│ event (click, │       │ navigates to  │       │ without reload│
│ form submit)  │       │ new route     │       │               │
└───────────────┘       └───────────────┘       └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Next.js Routing Basics
🤔
Concept: Learn how Next.js uses files in the pages folder to create routes automatically.
Next.js creates a page for each file inside the pages directory. For example, pages/index.js is the home page at '/'. pages/about.js is at '/about'. You navigate by clicking links that point to these routes.
Result
You can visit different pages by clicking links or typing URLs matching the file names.
Knowing how Next.js maps files to URLs helps you understand what useRouter will control when navigating programmatically.
2
FoundationUsing the Link Component for Navigation
🤔
Concept: Learn how to navigate between pages using the Link component in Next.js.
The Link component wraps an anchor tag and lets users click to go to another page without full reload. Example: About changes the page to '/about' smoothly.
Result
Clicking the link changes the page instantly without reloading the browser.
Link shows the default way users navigate, which programmatic navigation will replicate but controlled by code.
3
IntermediateIntroducing useRouter Hook
🤔
Concept: Learn how to get the router object inside a component using useRouter.
Import useRouter from 'next/router' and call it inside your component: const router = useRouter(). This router object has methods to navigate, like router.push('/path').
Result
You have access to functions that let you change pages by calling code, not just clicking links.
Understanding that useRouter gives you control over navigation lets you build dynamic flows based on user actions.
4
IntermediateNavigating Programmatically with router.push
🤔Before reading on: do you think router.push reloads the whole page or changes route smoothly? Commit to your answer.
Concept: Learn how to use router.push to change pages smoothly in response to events.
Call router.push('/new-page') inside event handlers like button clicks or form submissions. This changes the URL and loads the new page without a full reload, keeping the app fast.
Result
The app navigates to the new page instantly when the code runs.
Knowing router.push changes pages without reload helps you create seamless user experiences.
5
IntermediateUsing router.replace to Avoid History Entries
🤔Before reading on: do you think router.replace adds a new browser history entry or replaces the current one? Commit to your answer.
Concept: Learn how router.replace changes pages without adding a new entry to browser history.
router.replace('/path') navigates like push but replaces the current history entry. This means the back button skips the replaced page, useful for redirects after login or form submission.
Result
Navigation happens without creating a new back button step.
Understanding history behavior helps you control user navigation flow and back button experience.
6
AdvancedNavigating with Query Parameters and Dynamic Routes
🤔Before reading on: do you think you can pass query parameters with router.push as an object or only as a string URL? Commit to your answer.
Concept: Learn how to navigate to dynamic routes and pass query parameters programmatically.
You can pass an object to router.push: router.push({ pathname: '/post/[id]', query: { id: '123' } }) to navigate to dynamic routes. This keeps URLs clean and parameters structured.
Result
The app navigates to '/post/123' with query data accessible in the page.
Knowing how to pass queries and dynamic params programmatically unlocks flexible routing patterns.
7
ExpertHandling Navigation Events and Middleware Effects
🤔Before reading on: do you think useRouter navigation triggers lifecycle events you can listen to? Commit to your answer.
Concept: Learn about router events like routeChangeStart and how middleware can affect programmatic navigation.
useRouter emits events during navigation you can listen to for loading states or analytics. Middleware can intercept navigation to protect routes or redirect users. Programmatic navigation triggers these events just like user clicks.
Result
You can react to navigation changes and control access dynamically.
Understanding navigation events and middleware integration helps build robust, secure apps with smooth user flows.
Under the Hood
useRouter works by interacting with Next.js's client-side router. When you call router.push or router.replace, it updates the browser's history and URL using the HTML5 History API. Next.js then loads the new page's JavaScript and data in the background and swaps the content without a full page reload. This keeps the app fast and preserves state where possible.
Why designed this way?
Next.js was designed to combine the benefits of server-rendered pages with fast client-side navigation. Using useRouter and client-side routing avoids slow full page reloads and gives developers control over navigation flow. Alternatives like full reloads were too slow and broke app state, so this hybrid approach balances speed and SEO.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ router.push() │──────▶│ History API   │──────▶│ URL changes   │
│ or replace()  │       │ updates URL   │       │ without reload│
└───────────────┘       └───────────────┘       └───────────────┘
         │                        │                      │
         ▼                        ▼                      ▼
┌─────────────────────────────────────────────────────────┐
│ Next.js fetches new page code and data in background    │
│ then swaps page content smoothly without full reload    │
└─────────────────────────────────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does router.push cause a full page reload like a normal link? Commit to yes or no.
Common Belief:router.push reloads the entire page just like clicking a normal link.
Tap to reveal reality
Reality:router.push changes the URL and loads the new page client-side without a full reload, making navigation faster.
Why it matters:Believing this causes developers to avoid programmatic navigation, missing out on smoother user experiences.
Quick: Does router.replace add a new entry to browser history? Commit to yes or no.
Common Belief:router.replace works the same as router.push and adds a new history entry.
Tap to reveal reality
Reality:router.replace replaces the current history entry, so the back button skips the replaced page.
Why it matters:Misusing replace can confuse users with unexpected back button behavior or navigation loops.
Quick: Can you only navigate to static routes with useRouter? Commit to yes or no.
Common Belief:useRouter can only navigate to fixed, static routes defined by file names.
Tap to reveal reality
Reality:useRouter supports dynamic routes and query parameters, allowing flexible navigation to pages with variables.
Why it matters:Not knowing this limits app design and prevents building dynamic, data-driven pages.
Quick: Does programmatic navigation trigger router events like routeChangeStart? Commit to yes or no.
Common Belief:Only user clicks trigger router events, not programmatic navigation.
Tap to reveal reality
Reality:Programmatic navigation triggers the same router events, enabling consistent handling of loading states and analytics.
Why it matters:Ignoring this leads to missing important hooks for user feedback and monitoring.
Expert Zone
1
router.push accepts both string URLs and object forms with pathname and query, but mixing them incorrectly can cause subtle bugs.
2
Using shallow routing with router.push lets you change the URL without running getServerSideProps or getStaticProps again, improving performance in some cases.
3
Programmatic navigation inside useEffect or async functions requires careful handling to avoid memory leaks or navigation race conditions.
When NOT to use
Avoid programmatic navigation for simple static links where the Link component suffices. For complex state-driven navigation, consider state management libraries or server actions. Also, avoid programmatic navigation during server-side rendering as it only works client-side.
Production Patterns
In real apps, useRouter is used for redirects after login/logout, conditional navigation based on user roles, wizard-style multi-step forms, and analytics tracking on route changes. Combining router events with middleware enables secure and smooth user flows.
Connections
HTML5 History API
useRouter builds on the History API to change URLs without reloads
Understanding the History API clarifies how client-side routing works under the hood and why navigation feels instant.
State Machines
Programmatic navigation models app states and transitions like a state machine
Viewing navigation as state transitions helps design predictable user flows and handle edge cases.
Remote Controls (Electronics)
Both useRouter and remotes send commands to change states without physical interaction
Recognizing this pattern across domains shows how indirect control improves user convenience and system design.
Common Pitfalls
#1Calling router.push with a relative URL string that does not start with '/'
Wrong approach:router.push('about')
Correct approach:router.push('/about')
Root cause:router.push expects absolute paths starting with '/', otherwise navigation may fail or go to unexpected URLs.
#2Using router.push inside a component without checking if router is ready
Wrong approach:const router = useRouter(); router.push('/home'); // runs immediately
Correct approach:const router = useRouter(); useEffect(() => { if(router.isReady) router.push('/home'); }, [router.isReady]);
Root cause:Router object may not be fully initialized on first render, causing navigation errors if called too early.
#3Using router.replace when you want users to be able to go back
Wrong approach:router.replace('/dashboard')
Correct approach:router.push('/dashboard')
Root cause:replace removes the current history entry, so users cannot go back to the previous page, which may confuse navigation.
Key Takeaways
Programmatic navigation with useRouter lets your Next.js app change pages by code, creating dynamic user flows.
router.push changes pages smoothly without full reloads, improving speed and user experience.
router.replace navigates without adding history entries, useful for redirects but can affect back button behavior.
useRouter supports dynamic routes and query parameters, enabling flexible and data-driven navigation.
Understanding router events and middleware integration helps build secure, responsive apps with controlled navigation.