Skip to content

Next.js - Imperitive Routing

Next.js - Programmatic Navigation (Imperative Routing)

Section titled “Next.js - Programmatic Navigation (Imperative Routing)”

While the next/link component is the standard way to handle declarative navigation between pages (using <a> tags), Next.js also allows you to navigate programmatically, often referred to as imperative routing.

This is useful when you need to trigger navigation based on user actions (like form submissions), conditional logic, or after asynchronous operations complete.

The primary way to achieve this is by using the useRouter hook.

  • In the Pages Router, import useRouter from 'next/router'. It provides methods like router.push(), router.replace(), router.back(), etc.

  • In the App Router, import useRouter from 'next/navigation'. It offers similar methods (push, replace, back, forward, refresh). Note that this hook should only be used in Client Components.

Let’s modify an example page (using the Pages Router context) to demonstrate programmatic navigation with useRouter.

Consider this updated pages/index.js:

import { useRouter } from 'next/router';
import Head from 'next/head';
function HomePage(props) {
// Get the router object
const router = useRouter();
const goToFirstPost = () => {
console.log('Navigating to /posts/first-post programmatically...');
router.push('/posts/first-post');
};
const goToPostById = (postId) => {
console.log(`Navigating to /posts/${postId}...`);
// Example with dynamic route
router.push(`/posts/${postId}`);
// Or using the object syntax for more options (e.g., query params):
// router.push({
// pathname: '/posts/[id]',
// query: { id: postId, source: 'button_click' },
// });
};
return (
<>
<Head>
<title>Welcome to Next.js! (Imperative Routing)</title>
</Head>
<div>
<h1>Welcome to Next.js!</h1>
<p>Demonstrating Programmatic Navigation</p>
{/* Button triggering programmatic navigation */}
<button onClick={goToFirstPost}>
Go to First Post (using router.push)
</button>
<br />
<br />
<button onClick={() => goToPostById('my-dynamic-post')}>
Go to Dynamic Post (ID: my-dynamic-post)
</button>
<br />
<br />
<div>
GitHub Stars (from props): {props.stars}
</div>
</div>
</>
);
}
// Example data fetching (unrelated to routing itself)
export async function getServerSideProps(context) {
const res = await fetch('https://api.github.com/repos/vercel/next.js');
const json = await res.json();
return {
props: { stars: json.stargazers_count || 'N/A' },
};
}
export default HomePage;

In this example:

  • We import useRouter from 'next/router'.
  • Inside the component, const router = useRouter() gives access to the router instance.
  • The goToFirstPost function calls router.push('/posts/first-post') when the first button is clicked.
  • The goToPostById function demonstrates navigating to a dynamic route (/posts/[id].js) using router.push with a template literal or the object syntax.

Start the server:

npm run dev
# Example Output:
# ready - started server on 0.0.0.0:3000, url: http://localhost:3000
  1. Open http://localhost:3000 in your browser.

  2. Click the ‘Go to First Post (using router.push)’ button. You should be navigated to the /posts/first-post page, just as if you had clicked a <Link> component. Check your browser’s developer console for the log message.

  3. Navigate back to the home page.

  4. Click the ‘Go to Dynamic Post (ID: my-dynamic-post)’ button. Assuming you have a page setup at pages/posts/[id].js, you should be navigated to /posts/my-dynamic-post. The dynamic parameter my-dynamic-post would be available within that page component via router.query.id.

router.push() adds the new URL to the browser’s history stack, while router.replace() navigates without adding a new entry, effectively replacing the current URL in the history.