Code To Learn logo

Code To Learn

M4: Routes & Navigation

L9: Adding Navigation Links

Use Link components to navigate between pages without page reload

Right now, users have to manually type URLs to navigate between pages. Let's fix that by adding clickable links to listing cards!

React Router provides the Link component for navigation without page reloads - it's faster and preserves application state.

What You'll Learn

  • Use the Link component for navigation
  • Understand Link vs <a> tags
  • Add click handlers to navigate
  • Build dynamic link URLs
  • Navigate from card to details page

Regular HTML <a> tag:

<a href="/listing/42">View Listing</a>

How it works:

  • Full page reload
  • Loses React state
  • Re-downloads JavaScript
  • Re-initializes app
  • Slower

Use for external links only!

FeatureLink<a> tag
Page reload❌ No✅ Yes
Speed⚡ Fast🐌 Slow
State preserved✅ Yes❌ No
React Router✅ Yes❌ No
External sites❌ No✅ Yes

Rule of thumb:

  • Internal navigation → <Link>
  • External sites → <a>

Step 1: Update PropertyCard

Let's make listing cards clickable:

Open PropertyCard.jsx and import Link:

src/components/PropertyCard.jsx
import { Link } from 'react-router-dom'; 

export default function PropertyCard({ listing }) {
  return (
    <Link to={`/listing/${listing.id}`} className="block"> {}
      <div className="border border-gray-200 rounded-lg overflow-hidden hover:shadow-lg transition-shadow">
        {/* Image */}
        <div className="h-48 bg-gray-200"></div>

        {/* Content */}
        <div className="p-4">
          <h3 className="font-semibold text-lg mb-1">{listing.title}</h3>
          <p className="text-gray-600 text-sm mb-2">{listing.location}</p>
          <div className="flex items-center justify-between">
            <span className="text-gray-900 font-semibold">
              ${listing.price} / night
            </span>
            <span className="text-sm text-gray-600">
              ⭐ {listing.rating}
            </span>
          </div>
        </div>
      </div>
    </Link> {}
  );
}

What changed:

  • Wrapped entire card in <Link> component
  • Used template literal for dynamic URL: /listing/${listing.id}
  • Added block class for proper styling
  • Removed old click handler (if any)

Test Navigation

  1. Go to homepage (http://localhost:5173/)
  2. Click any listing card
  3. Should navigate to listing details
  4. Notice: No page reload!
  5. Browser back button works
  6. URL updates in address bar

Building Dynamic URLs

The key part is the to prop with template literal:

<Link to={`/listing/${listing.id}`}>

How it works:

// If listing.id = 1
to={`/listing/${listing.id}`}  // → "/listing/1"

// If listing.id = 42
to={`/listing/${listing.id}`}  // → "/listing/42"

// If listing.id = 999
to={`/listing/${listing.id}`}  // → "/listing/999"

Each card links to its own details page!

Add hover effects to show cards are clickable:

Enhanced PropertyCard with Hover
<Link to={`/listing/${listing.id}`} className="block group">
  <div className="border border-gray-200 rounded-lg overflow-hidden hover:shadow-lg transition-shadow cursor-pointer">
    {/* Content */}
    <div className="p-4">
      <h3 className="font-semibold text-lg mb-1 group-hover:text-pink-600 transition-colors">
        {listing.title}
      </h3>
      <p className="text-gray-600 text-sm mb-2">{listing.location}</p>
      {/* ... */}
    </div>
  </div>
</Link>

Hover effects:

  • Shadow increases on hover
  • Title changes color
  • Cursor becomes pointer
  • Smooth transitions

User flow:

1. User on homepage (/)

2. Clicks listing card (#42)

3. Link navigates to /listing/42

4. No page reload!

5. ListingDetailsPage loads

6. useParams extracts id = "42"

7. Fetches listing #42 data

8. Displays listing details

All in-app, no full page reloads!

For programmatic navigation:

import { useNavigate } from 'react-router-dom';

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

  const handleSubmit = () => {
    // Do something
    navigate('/success');
  };

  return <button onClick={handleSubmit}>Submit</button>;
}

Best for:

  • After form submissions
  • Conditional redirects
  • After API calls
  • Timeout-based navigation

Use Link when:

  • User clicks to navigate
  • Need crawlable links (SEO)
  • Standard navigation patterns
  • Want right-click → open in new tab

Use useNavigate when:

  • After form submission
  • Conditional logic (if/else)
  • Redirect after login
  • Navigate after timer

Example:

// ✅ Link - user click
<Link to="/profile">Go to Profile</Link>

// ✅ useNavigate - after action
function LoginForm() {
  const navigate = useNavigate();

  const handleLogin = async () => {
    await login();
    navigate('/dashboard'); // Redirect after login
  };

  return <button onClick={handleLogin}>Login</button>;
}

Navigation Complete!

Users can now click listing cards to view details! Your app has proper navigation without page reloads - it feels like a real single-page application.

Quick Recap

What we accomplished:

  • ✅ Added Link components to listing cards
  • ✅ Built dynamic URLs with template literals
  • ✅ Enabled click-to-navigate functionality
  • ✅ Preserved app state during navigation
  • ✅ Added hover effects for better UX

Key concepts:

  • Link component - Client-side navigation
  • to prop - Destination path
  • Dynamic URLs - Template literals with IDs
  • No page reload - Faster, preserves state
  • Link vs <a> - Internal vs external

What's Next?

In Lesson 10, we'll create a NotFoundPage component for handling 404 errors when users visit invalid routes. Every app needs graceful error handling! 🚫