How to add links in the Nextjs App Router?

In Next.js 14, navigation between different pages of your app is made simple with the built-in Link component. Whether you’re linking between static pages, dynamic routes, or even navigating programmatically, Next.js provides flexible options for building a smooth and efficient user experience. In this tutorial, we’ll explore how to use Link, handle dynamic navigation, and even preload routes for better performance.

Using the 'Link' Component for Navigation

The Link component is the go-to method for navigating between pages in Next.js. It optimizes the user experience by handling client-side navigation, which means it loads pages without refreshing the browser.

Let’s take an example from our Recipe App. We’ll add links to navigate between the Home, Recipes, and Dashboard pages.

// src/app/layout.tsx
import Link from 'next/link';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <header>
          <h1>Recipe App</h1>
          <nav>
            <Link href="/">Home</Link> | <Link href="/recipes">Recipes</Link> | <Link href="/dashboard">Dashboard</Link>
          </nav>
        </header>
        <main>{children}</main>
        <footer>© 2024 Recipe App</footer>
      </body>
    </html>
  );
}

In this example:

  • The Link component wraps each navigation link (Home, Recipes, Dashboard).
  • Instead of a full page reload, Next.js automatically performs client-side navigation, keeping things smooth and fast.

Final HTML output for the home page:

<html lang="en">
  <body>
    <header>
      <h1>Recipe App</h1>
      <nav>
        <a href="/">Home</a> | <a href="/recipes">Recipes</a> | <a href="/dashboard">Dashboard</a>
      </nav>
    </header>
    <main>
      <section>
        <h2>Welcome to the Recipe App</h2>
        <p>Find the best recipes and share your own!</p>
      </section>
    </main>
    <footer>© 2024 Recipe App</footer>
  </body>
</html>

Linking Dynamic Routes

How to navigate Dynamic Routes in Next.js?

Dynamic routes are common in apps like the Recipe App. For example, each recipe has a unique ID, and we want to navigate to these pages based on that ID.

Let’s create a link to a specific recipe page:

// src/app/recipes/page.tsx
import Link from 'next/link';

export default function RecipesPage() {
  return (
    <section>
      <h2>All Recipes</h2>
      <ul>
        <li><Link href="/recipes/chocolate-cake">Chocolate Cake</Link></li>
        <li><Link href="/recipes/pasta">Pasta</Link></li>
      </ul>
    </section>
  );
}

Now, clicking on "Chocolate Cake" will navigate the user to /recipes/chocolate-cake without refreshing the page.

Final HTML output for /recipes:

<html lang="en">
  <body>
    <header>
      <h1>Recipe App</h1>
      <nav>
        <a href="/">Home</a> | <a href="/recipes">Recipes</a> | <a href="/dashboard">Dashboard</a>
      </nav>
    </header>
    <main>
      <section>
        <h2>All Recipes</h2>
        <ul>
          <li><a href="/recipes/chocolate-cake">Chocolate Cake</a></li>
          <li><a href="/recipes/pasta">Pasta</a></li>
        </ul>
      </section>
    </main>
    <footer>© 2024 Recipe App</footer>
  </body>
</html>

Client-Side Navigation

How to add links at client side in the Next.js?

Sometimes, you might want to navigate programmatically based on certain actions (like submitting a form). You can do this using the useRouter hook from Next.js.

Here’s an example where we programmatically navigate to the newly created recipe page after a form is submitted:

// src/app/dashboard/add-recipe/page.tsx
'use client';

import { useRouter } from 'next/navigation';
import { useState } from 'react';

export default function AddRecipePage() {
  const router = useRouter();
  const [recipeName, setRecipeName] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // Let's assume the recipe name is converted to a slug
    const slug = recipeName.toLowerCase().replace(/ /g, '-');
    router.push(`/recipes/${slug}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="recipeName">Recipe Name:</label>
      <input
        id="recipeName"
        type="text"
        value={recipeName}
        onChange={(e) => setRecipeName(e.target.value)}
      />
      <button type="submit">Add Recipe</button>
    </form>
  );
}

Here:

  • We use useRouter to access the router.push method, which navigates to the new recipe’s page after submission.
  • The recipe name is converted to a URL-friendly slug, like /recipes/chocolate-cake.

Preloading Pages

How to preload pages from Links in Next.js?

Next.js can preload linked pages in the background to make navigation even faster. By default, Link preloads the pages it points to, but you can control this behavior if needed.

For example, let’s disable preloading for a specific link:

<Link href="/recipes" prefetch={false}>Recipes</Link>

This ensures that the /recipes page won’t be preloaded until the user actually clicks on the link. Preloading is particularly useful for high-traffic pages, but you can disable it for lesser-used ones to optimize resource usage.


Next.js’s Link component can also handle external links. By default, it assumes you’re linking to an internal route. If you need to link to an external website, you’ll still use Link, but with additional attributes for safety.

<Link href="https://example.com" target="_blank" rel="noopener noreferrer">
  Visit Example
</Link>
  • target="_blank" ensures the link opens in a new tab.
  • rel="noopener noreferrer" is important for security, preventing the new page from accessing the original page’s window object.

Apologies for the confusion! Let’s strictly focus on Next.js 14 App Router. Here’s a corrected version of the additional sections, tailored specifically for Next.js 14 App Router and its caching and navigation mechanisms:


Cache Management in Navigation

In Next.js 14 App Router, caching is handled at the data-fetching level, allowing you to control the caching behavior of Server Components and API requests. To manage caching, you can use the fetch function's caching options like cache or revalidate. For example, when fetching data inside a Server Component, you can specify how long to cache the data or when to revalidate it.

// Example of using cache in a Server Component
export default async function RecipesPage() {
  const recipes = await fetch('https://api.example.com/recipes', {
    cache: 'force-cache', // Cache the data aggressively
  }).then((res) => res.json());

  return (
    <section>
      <h2>All Recipes</h2>
      <ul>
        {recipes.map((recipe) => (
          <li key={recipe.id}>{recipe.name}</li>
        ))}
      </ul>
    </section>
  );
}

Alternatively, if you want the data to revalidate after a certain time, you can specify revalidate:

// Example with revalidate
export default async function RecipesPage() {
  const recipes = await fetch('https://api.example.com/recipes', {
    next: { revalidate: 60 }, // Revalidate the data every 60 seconds
  }).then((res) => res.json());

  return (
    <section>
      <h2>All Recipes</h2>
      <ul>
        {recipes.map((recipe) => (
          <li key={recipe.id}>{recipe.name}</li>
        ))}
      </ul>
    </section>
  );
}

This ensures that the page’s content is cached, but Next.js will revalidate the cache every 60 seconds, making sure users always get fresh data.


Soft Navigation

In Next.js 14 App Router, soft navigation is enabled by default through client-side navigation. When you use the Link component, Next.js does not reload the entire page but instead swaps the content on the client side. This behavior makes navigation feel smooth and fast, similar to Single Page Applications (SPAs).

// Example of soft navigation
<Link href="/recipes">Recipes</Link>

Here, navigating from one page to another (like Home to Recipes) is done without triggering a full-page reload, improving the user experience by keeping transitions fast and fluid.


Back Navigation

With Next.js 14 App Router, you can use the useRouter hook to programmatically navigate between routes, including handling "back" navigation. This is useful in scenarios where you want to give users the option to return to the previous page.

'use client';

import { useRouter } from 'next/navigation';

export default function RecipeDetail() {
  const router = useRouter();

  const goBack = () => {
    router.back(); // Go back to the previous page
  };

  return (
    <div>
      <h2>Recipe Detail</h2>
      <button onClick={goBack}>Go Back</button>
    </div>
  );
}

The router.back() method works like the browser’s back button, navigating users to their previous page. This enhances the navigation flow, especially for multi-page interactions like a recipe app.

Note: You have already seen router.push() for forward navigation.


Summary

Linking and navigating in Next.js 14 is straightforward thanks to the Link component and the useRouter hook. By utilizing these tools, you can seamlessly navigate between pages, handle dynamic routes, and even manage client-side navigation. Whether you’re building a simple app or a complex site, mastering navigation in Next.js will enhance the user experience and performance of your app.

Next.js
Clap here if you liked the blog