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
Link vs Anchor Tag
React Router Link:
import { Link } from 'react-router-dom';
<Link to="/listing/42">View Listing</Link>How it works:
- Prevents page reload
- Uses History API
- Preserves React state
- Updates URL
- Fast navigation
Perfect for internal navigation!
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!
| Feature | Link | <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:
Add Link to PropertyCard
Open PropertyCard.jsx and import Link:
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
blockclass for proper styling - Removed old click handler (if any)
Test Navigation
- Go to homepage (
http://localhost:5173/) - Click any listing card
- Should navigate to listing details
- Notice: No page reload!
- Browser back button works
- 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!
Styling Interactive Links
Add hover effects to show cards are clickable:
<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
Link Props and Options
Common Link Patterns
Navigation in Action
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 detailsAll in-app, no full page reloads!
Link vs useNavigate
For user-initiated clicks:
<Link to="/listing/42">
<button>View Details</button>
</Link>Best for:
- Clickable elements
- Cards, buttons, text links
- Standard navigation
- Keyboard accessible
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>;
}Debugging Link Issues
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! 🚫