Programmatic Navigation

Introduction

In modern React applications, navigation is not always triggered by clicking links. Often, developers need to navigate programmatically in response to user actions, such as submitting a form, logging in, completing a process, or dynamically redirecting users based on application logic.

React Router provides a useNavigate hook that enables programmatic navigation in functional components. This post will cover:

  1. Using the useNavigate hook for navigation
  2. Navigating after events like form submission
  3. Example: Redirecting users after successful login
  4. Replacing history entries versus pushing new entries

1. Using useNavigate Hook for Navigation

The useNavigate hook is provided by React Router v6+ and replaces the older useHistory hook from previous versions. It returns a function that allows you to navigate programmatically.

Basic Usage

import React from "react";
import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();

  const goToAbout = () => {
navigate("/about");
}; return (
<div>
  <h1>Home Page</h1>
  <button onClick={goToAbout}>Go to About</button>
</div>
); } export default Home;

Explanation:

  • useNavigate returns a navigate function.
  • Calling navigate("/about") changes the route to /about.
  • Navigation can be triggered on any event (click, form submit, API success, etc.).

Navigate with Options

navigate can take a second argument with options:

navigate("/about", { replace: true, state: { from: "/home" } });
  • replace: true replaces the current entry in the history stack.
  • state allows passing arbitrary data to the next route.

Example:

navigate("/dashboard", { replace: true, state: { userId: 123 } });
  • Useful for redirecting users without leaving a back entry in history (e.g., after login).

2. Navigating After Events

Programmatic navigation is often used after user actions or events.

Example: Form Submission

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";

function ContactForm() {
  const [name, setName] = useState("");
  const navigate = useNavigate();

  const handleSubmit = (e) => {
e.preventDefault();
console.log("Form submitted:", name);
// After submission, navigate to thank-you page
navigate("/thank-you");
}; return (
<form onSubmit={handleSubmit}>
  <input
    type="text"
    value={name}
    onChange={(e) => setName(e.target.value)}
    placeholder="Your Name"
  />
  <button type="submit">Submit</button>
</form>
); } export default ContactForm;

Explanation:

  • After form submission, navigate("/thank-you") programmatically redirects the user.
  • No need for a <Link> component.

Conditional Navigation

You can navigate based on conditions or API responses.

const handleLogin = async () => {
  const response = await fakeLoginAPI(username, password);
  if (response.success) {
navigate("/dashboard");
} else {
alert("Login failed");
} };
  • Navigation happens only if certain conditions are met.

3. Example: Redirecting Users After Successful Login

Step 1: Login Component

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";

function Login() {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const navigate = useNavigate();

  const handleLogin = (e) => {
e.preventDefault();
// Fake authentication logic
if (username === "admin" &amp;&amp; password === "1234") {
  navigate("/dashboard", { replace: true });
} else {
  alert("Invalid credentials");
}
}; return (
&lt;form onSubmit={handleLogin}&gt;
  &lt;input
    type="text"
    placeholder="Username"
    value={username}
    onChange={(e) =&gt; setUsername(e.target.value)}
  /&gt;
  &lt;input
    type="password"
    placeholder="Password"
    value={password}
    onChange={(e) =&gt; setPassword(e.target.value)}
  /&gt;
  &lt;button type="submit"&gt;Login&lt;/button&gt;
&lt;/form&gt;
); } export default Login;

Step 2: Dashboard Component

import React from "react";

function Dashboard() {
  return <h1>Welcome to the Dashboard!</h1>;
}

export default Dashboard;

Step 3: Routing Setup

import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./Login";
import Dashboard from "./Dashboard";

function App() {
  return (
&lt;Router&gt;
  &lt;Routes&gt;
    &lt;Route path="/" element={&lt;Login /&gt;} /&gt;
    &lt;Route path="/dashboard" element={&lt;Dashboard /&gt;} /&gt;
  &lt;/Routes&gt;
&lt;/Router&gt;
); } export default App;

Explanation:

  • After successful login, the user is redirected to /dashboard.
  • replace: true ensures that hitting the back button does not return the user to the login page.

4. Replacing History vs Pushing New Entries

Push New Entry

navigate("/dashboard");
  • Adds a new entry to the browser history stack.
  • Users can use the back button to return to the previous page.
  • Useful when you want to maintain navigation history.

Replace Current Entry

navigate("/dashboard", { replace: true });
  • Replaces the current history entry.
  • Users cannot go back to the previous route using the back button.
  • Ideal for login or redirect after submission where returning to the previous page is not desired.

Comparison:

MethodHistory Stack BehaviorUse Case
Push (default)Adds a new entryNormal navigation between pages
Replace (true)Replaces current entryLogin redirect, after form submission

5. Navigating with State

useNavigate can pass state to the target route.

navigate("/dashboard", { state: { userId: 123, username: "admin" } });

Accessing state in the target component:

import { useLocation } from "react-router-dom";

function Dashboard() {
  const location = useLocation();
  console.log(location.state); // { userId: 123, username: "admin" }
  return <h1>Dashboard for {location.state.username}</h1>;
}
  • State is useful for passing non-URL information between routes.

6. Best Practices for Programmatic Navigation

  1. Use replace: true for login redirects – Prevents users from navigating back to the login page.
  2. Use relative paths – Especially in nested routes.
  3. Handle edge cases – Check authentication before redirecting to protected routes.
  4. Pass state carefully – Avoid sensitive information in state; use context or Redux for persistent global state.
  5. Avoid navigating during render – Always navigate in event handlers or effects.

7. Real-World Use Cases

  • Redirecting users after login or logout
  • Navigating to confirmation or thank-you pages after form submission
  • Conditional navigation based on user roles or permissions
  • Dynamic routing in wizards or multi-step forms

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *