Code To Learn logo

Code To Learn

M6: State ManagementRedux Toolkit Path

L10: Navbar Component

Create a Navbar with navigation links and favorites count

Let's create a navigation bar to help users navigate between pages!

What We're Building

A Navbar component with:

  1. Home link
  2. Favorites link with count badge
  3. Responsive layout
  4. Professional styling

Step 1: Create Navbar File

Create a new component file:

touch src/components/Navbar.jsx

Step 2: Build the Navbar

Add the basic structure:

src/components/Navbar.jsx
import { Link } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { Home, Heart } from 'lucide-react';

function Navbar() {
  const favoriteCount = useSelector((state) => state.listings.favorites.length);
  
  return (
    <nav className="bg-white border-b border-gray-200">
      <div className="container mx-auto px-4">
        <div className="flex items-center justify-between h-16">
          {/* Logo/Brand */}
          <Link to="/" className="text-xl font-bold text-gray-900">
            StayScape
          </Link>
          
          {/* Navigation Links */}
          <div className="flex items-center space-x-6">
            <Link 
              to="/" 
              className="flex items-center space-x-2 text-gray-600 hover:text-gray-900"
            >
              <Home size={20} />
              <span>Home</span>
            </Link>
            
            <Link 
              to="/favorites" 
              className="flex items-center space-x-2 text-gray-600 hover:text-gray-900 relative"
            >
              <Heart size={20} />
              <span>Favorites</span>
              {favoriteCount > 0 && (
                <span className="absolute -top-1 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
                  {favoriteCount}
                </span>
              )}
            </Link>
          </div>
        </div>
      </div>
    </nav>
  );
}

export default Navbar;

What's happening here?

Imports

import { Link } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { Home, Heart } from 'lucide-react';
  • Link - Navigation without page reload
  • useSelector - Read favorites count from Redux
  • Home, Heart - Icons from lucide-react

Get Favorite Count

const favoriteCount = useSelector((state) => state.listings.favorites.length);

Reads the length of the favorites array:

  • state.listings.favorites - Array of IDs
  • .length - Number of items in array

Examples:

  • []0
  • [1, 5]2
  • [1, 3, 5, 7, 9]5
<nav className="bg-white border-b border-gray-200">
  <div className="container mx-auto px-4">
    <div className="flex items-center justify-between h-16">
      {/* Content */}
    </div>
  </div>
</nav>

Structure:

  • <nav> - Semantic HTML for navigation
  • container mx-auto - Centered max-width container
  • flex justify-between - Space between logo and links
  • h-16 - Fixed height of 64px
<Link to="/" className="text-xl font-bold text-gray-900">
  StayScape
</Link>

Clicking the logo takes you to the homepage!

<div className="flex items-center space-x-6">
  <Link to="/" className="flex items-center space-x-2">
    <Home size={20} />
    <span>Home</span>
  </Link>
  
  <Link to="/favorites" className="flex items-center space-x-2">
    <Heart size={20} />
    <span>Favorites</span>
  </Link>
</div>

Each link:

  • Icon + text
  • Hover effect (text-gray-600 hover:text-gray-900)
  • Flexbox for alignment

Favorites Count Badge

{favoriteCount > 0 && (
  <span className="absolute -top-1 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
    {favoriteCount}
  </span>
)}

Only shows if there are favorites!

  • absolute positioning relative to link
  • Red circle badge
  • Centered number
  • Top-right corner placement

Understanding the Badge Logic

The badge uses conditional rendering:

{favoriteCount > 0 && (
  <span>Badge</span>
)}

How it works:

Let's highlight the active link:

src/components/Navbar.jsx
import { Link, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { Home, Heart } from 'lucide-react';

function Navbar() {
  const location = useLocation();
  const favoriteCount = useSelector((state) => state.listings.favorites.length);
  
  const isActive = (path) => location.pathname === path;
  
  return (
    <nav className="bg-white border-b border-gray-200">
      <div className="container mx-auto px-4">
        <div className="flex items-center justify-between h-16">
          <Link to="/" className="text-xl font-bold text-gray-900">
            StayScape
          </Link>
          
          <div className="flex items-center space-x-6">
            <Link 
              to="/" 
              className={`flex items-center space-x-2 ${
                isActive('/') 
                  ? 'text-blue-600 font-semibold' 
                  : 'text-gray-600 hover:text-gray-900'
              }`}
            >
              <Home size={20} />
              <span>Home</span>
            </Link>
            
            <Link 
              to="/favorites" 
              className={`flex items-center space-x-2 relative ${
                isActive('/favorites') 
                  ? 'text-blue-600 font-semibold' 
                  : 'text-gray-600 hover:text-gray-900'
              }`}
            >
              <Heart size={20} />
              <span>Favorites</span>
              {favoriteCount > 0 && (
                <span className="absolute -top-1 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
                  {favoriteCount}
                </span>
              )}
            </Link>
          </div>
        </div>
      </div>
    </nav>
  );
}

export default Navbar;

What changed?

Import useLocation

import { Link, useLocation } from 'react-router-dom';

useLocation gives us the current URL path.

Get Current Location

const location = useLocation();

location.pathname examples:

  • On homepage: "/"
  • On favorites: "/favorites"
  • On listing details: "/listings/123"

Helper Function

const isActive = (path) => location.pathname === path;

Returns true if we're on that path:

isActive('/');         // true on homepage, false elsewhere
isActive('/favorites'); // true on favorites, false elsewhere

Conditional Styling

className={`flex items-center space-x-2 ${
  isActive('/') 
    ? 'text-blue-600 font-semibold'    // Active: blue and bold
    : 'text-gray-600 hover:text-gray-900' // Inactive: gray
}`}

Active link:

  • Blue color (text-blue-600)
  • Bold text (font-semibold)

Inactive link:

  • Gray color (text-gray-600)
  • Hover effect (hover:text-gray-900)

Responsive Mobile Navbar

For a complete navbar, add mobile responsiveness:

src/components/Navbar.jsx
import { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { Home, Heart, Menu, X } from 'lucide-react';

function Navbar() {
  const [isOpen, setIsOpen] = useState(false);
  const location = useLocation();
  const favoriteCount = useSelector((state) => state.listings.favorites.length);
  
  const isActive = (path) => location.pathname === path;
  
  return (
    <nav className="bg-white border-b border-gray-200">
      <div className="container mx-auto px-4">
        <div className="flex items-center justify-between h-16">
          {/* Logo */}
          <Link to="/" className="text-xl font-bold text-gray-900">
            StayScape
          </Link>
          
          {/* Desktop Navigation */}
          <div className="hidden md:flex items-center space-x-6">
            <Link 
              to="/" 
              className={`flex items-center space-x-2 ${
                isActive('/') 
                  ? 'text-blue-600 font-semibold' 
                  : 'text-gray-600 hover:text-gray-900'
              }`}
            >
              <Home size={20} />
              <span>Home</span>
            </Link>
            
            <Link 
              to="/favorites" 
              className={`flex items-center space-x-2 relative ${
                isActive('/favorites') 
                  ? 'text-blue-600 font-semibold' 
                  : 'text-gray-600 hover:text-gray-900'
              }`}
            >
              <Heart size={20} />
              <span>Favorites</span>
              {favoriteCount > 0 && (
                <span className="absolute -top-1 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
                  {favoriteCount}
                </span>
              )}
            </Link>
          </div>
          
          {/* Mobile Menu Button */}
          <button 
            onClick={() => setIsOpen(!isOpen)}
            className="md:hidden p-2"
          >
            {isOpen ? <X size={24} /> : <Menu size={24} />}
          </button>
        </div>
        
        {/* Mobile Navigation */}
        {isOpen && (
          <div className="md:hidden py-4 border-t border-gray-200">
            <Link 
              to="/" 
              onClick={() => setIsOpen(false)}
              className="flex items-center space-x-2 py-2 text-gray-600 hover:text-gray-900"
            >
              <Home size={20} />
              <span>Home</span>
            </Link>
            
            <Link 
              to="/favorites" 
              onClick={() => setIsOpen(false)}
              className="flex items-center space-x-2 py-2 text-gray-600 hover:text-gray-900"
            >
              <Heart size={20} />
              <span>Favorites ({favoriteCount})</span>
            </Link>
          </div>
        )}
      </div>
    </nav>
  );
}

export default Navbar;

Mobile features:

  • Hamburger menu button
  • Collapsible menu
  • Click link closes menu
  • Hidden on desktop (hidden md:flex)

Complete Simple Version

For now, use the simple desktop version:

src/components/Navbar.jsx
import { Link } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { Home, Heart } from 'lucide-react';

function Navbar() {
  const favoriteCount = useSelector((state) => state.listings.favorites.length);
  
  return (
    <nav className="bg-white border-b border-gray-200">
      <div className="container mx-auto px-4">
        <div className="flex items-center justify-between h-16">
          <Link to="/" className="text-xl font-bold text-gray-900">
            StayScape
          </Link>
          
          <div className="flex items-center space-x-6">
            <Link 
              to="/" 
              className="flex items-center space-x-2 text-gray-600 hover:text-gray-900"
            >
              <Home size={20} />
              <span>Home</span>
            </Link>
            
            <Link 
              to="/favorites" 
              className="flex items-center space-x-2 text-gray-600 hover:text-gray-900 relative"
            >
              <Heart size={20} />
              <span>Favorites</span>
              {favoriteCount > 0 && (
                <span className="absolute -top-1 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
                  {favoriteCount}
                </span>
              )}
            </Link>
          </div>
        </div>
      </div>
    </nav>
  );
}

export default Navbar;

What's Next?

Perfect! The Navbar is ready. In the next lesson, we'll:

  1. Add Navbar to App - Integrate it into the layout
  2. Test navigation - Verify links work
  3. See count update - Watch badge change as favorites change

✅ Lesson Complete! You've created a professional navigation bar with favorites count!

Key Takeaways

  • Link component for client-side navigation
  • useSelector to read favorites count from Redux
  • Conditional rendering with && for badge
  • Absolute positioning for badge placement
  • Icons from lucide-react for visual appeal
  • Flexbox layout for alignment
  • Hover states for better UX