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:
- Home link
- Favorites link with count badge
- Responsive layout
- Professional styling
Step 1: Create Navbar File
Create a new component file:
touch src/components/Navbar.jsxStep 2: Build the Navbar
Add the basic structure:
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 reloaduseSelector- Read favorites count from ReduxHome,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
Navbar Container
<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 navigationcontainer mx-auto- Centered max-width containerflex justify-between- Space between logo and linksh-16- Fixed height of 64px
Logo/Brand Link
<Link to="/" className="text-xl font-bold text-gray-900">
StayScape
</Link>Clicking the logo takes you to the homepage!
Navigation Links
<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!
absolutepositioning 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:
Adding Active Link Styling
Let's highlight the active link:
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 elsewhereConditional 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:
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:
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:
- Add Navbar to App - Integrate it into the layout
- Test navigation - Verify links work
- See count update - Watch badge change as favorites change
✅ Lesson Complete! You've created a professional navigation bar with favorites count!
Key Takeaways
- ✅
Linkcomponent for client-side navigation - ✅
useSelectorto 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