Code To Learn logo

Code To Learn

M4: Routes & Navigation

L4: Creating the ListingDetailsPage

Build a new page to display detailed information about individual listings

Time to build a details page! When users click on a listing card, they'll navigate to a dedicated page showing full information about that property. Let's create the ListingDetailsPage component.

What You'll Learn

  • How to create new page components
  • Structuring a details page
  • Preparing for dynamic routing
  • Best practices for page-level components

Why We Need a Details Page

Right now, your PropertyCard shows limited information:

  • Title
  • Location
  • Price per night
  • Rating and reviews
  • Small image carousel

But users need to see much more before booking:

  • 📸 Full image gallery - All property photos
  • 📝 Complete description - Detailed property info
  • 🛏️ Amenities - WiFi, kitchen, parking, etc.
  • 🏠 Property type - Entire place, private room, shared room
  • 👥 Host information - Name, profile, reviews
  • 📅 Availability calendar - Check-in/check-out dates
  • 💰 Pricing breakdown - Nightly rate, cleaning fee, service fee
  • Detailed reviews - Guest feedback and ratings
  • 📍 Map location - Where the property is located

A dedicated details page provides space for all this information!

Page vs Component

Before we build, let's understand the difference:

Page components represent entire routes/views:

Characteristics:

  • Live in src/pages/ folder
  • Represent full screens/routes
  • Handle their own data fetching
  • Usually named with "Page" suffix
  • Connected to routes in Router

Examples:

HomePage.jsx        -> /
ListingDetailsPage.jsx -> /listing/:id
ProfilePage.jsx     -> /profile
NotFoundPage.jsx    -> /404

Structure:

export default function ListingDetailsPage() {
  // Data fetching
  // State management
  // Layout
  
  return (
    <div className="page-container">
      {/* Page content with multiple components */}
    </div>
  );
}

Regular components are reusable pieces:

Characteristics:

  • Live in src/components/ folder
  • Represent UI pieces
  • Receive data via props
  • Named after what they display
  • Used within pages

Examples:

PropertyCard.jsx
ImageCarousel.jsx
Spinner.jsx
ErrorMessage.jsx

Structure:

export default function PropertyCard({ listing }) {
  // Just display logic
  
  return (
    <div className="card">
      {/* Card content */}
    </div>
  );
}

Planning the ListingDetailsPage

Let's plan what this page will contain:

ListingDetailsPage
├── Hero Section
│   └── Large image carousel (all listing images)
├── Main Content
│   ├── Title, location, rating
│   ├── Host information
│   ├── Description
│   └── Amenities list
└── Sidebar
    ├── Pricing card
    ├── Check-in/check-out dates
    ├── Guest selector
    └── "Reserve" button

For now, we'll create a basic structure and add features incrementally!

Step 1: Create the File

Create the Page File

Create a new file in the pages folder:

Terminal
touch src/pages/ListingDetailsPage.jsx

Your pages folder should now have:

HomePage.jsx
ListingDetailsPage.jsx

Step 2: Build the Basic Structure

Add the Component Skeleton

Start with a simple structure:

src/pages/ListingDetailsPage.jsx
export default function ListingDetailsPage() {
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold text-gray-900 mb-4">
        Listing Details
      </h1>
      
      <p className="text-gray-600">
        Detailed information about the listing will appear here.
      </p>
    </div>
  );
}

What we have:

  • Container with padding and margins
  • Heading for the page
  • Placeholder text

This is just a starting point - we'll add much more content soon!

Understanding the Layout Structure

Let's break down the JSX structure:

Step 3: Add a More Detailed Layout

Let's add more structure to prepare for actual content:

Create Content Sections

Update the page with proper sections:

src/pages/ListingDetailsPage.jsx
export default function ListingDetailsPage() {
  return (
    <div className="container mx-auto px-4 py-8">
      {/* Page Header */}
      <div className="mb-6">
        <h1 className="text-3xl font-bold text-gray-900 mb-2">
          Listing Title
        </h1>
        <div className="flex items-center gap-4 text-sm text-gray-600">
          <span>📍 Location</span>
          <span>⭐ Rating</span>
          <span>👥 Reviews</span>
        </div>
      </div>

      {/* Image Section - Placeholder */}
      <div className="bg-gray-200 rounded-lg h-96 mb-6 flex items-center justify-center">
        <p className="text-gray-500">Image Gallery Coming Soon</p>
      </div>

      {/* Main Content Grid */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
        {/* Left Column - Main Info */}
        <div className="lg:col-span-2 space-y-6">
          {/* Property Info */}
          <div>
            <h2 className="text-xl font-semibold mb-2">About this place</h2>
            <p className="text-gray-600">
              Property description will appear here with full details about the listing.
            </p>
          </div>

          {/* Amenities */}
          <div>
            <h2 className="text-xl font-semibold mb-2">What this place offers</h2>
            <div className="text-gray-600">
              Amenities list coming soon
            </div>
          </div>
        </div>

        {/* Right Column - Booking Card */}
        <div className="lg:col-span-1">
          <div className="border border-gray-200 rounded-lg p-6 sticky top-4">
            <div className="text-2xl font-bold mb-4">
              $100 <span className="text-base font-normal text-gray-600">/ night</span>
            </div>
            <p className="text-gray-600">Booking form coming soon</p>
          </div>
        </div>
      </div>
    </div>
  );
}

Understanding the Layout

Let's break down the structure:

Complete ListingDetailsPage Code

Here's the full component with all sections:

src/pages/ListingDetailsPage.jsx
export default function ListingDetailsPage() {
  return (
    <div className="container mx-auto px-4 py-8">
      {/* Page Header */}
      <div className="mb-6">
        <h1 className="text-3xl font-bold text-gray-900 mb-2">
          Cozy Beach House
        </h1>
        <div className="flex items-center gap-4 text-sm text-gray-600">
          <span>📍 Malibu, California</span>
          <span>⭐ 4.8</span>
          <span>👥 124 reviews</span>
        </div>
      </div>

      {/* Image Placeholder */}
      <div className="bg-gray-200 rounded-lg h-96 mb-6 flex items-center justify-center">
        <p className="text-gray-500 text-lg">Image Gallery Coming Soon</p>
      </div>

      {/* Main Content Grid */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
        {/* Left Column - Main Info */}
        <div className="lg:col-span-2 space-y-6">
          {/* Host Info */}
          <div className="border-b border-gray-200 pb-6">
            <h2 className="text-xl font-semibold mb-2">
              Entire place hosted by John
            </h2>
            <div className="text-gray-600">
              4 guests · 2 bedrooms · 3 beds · 2 bathrooms
            </div>
          </div>

          {/* Description */}
          <div className="border-b border-gray-200 pb-6">
            <h2 className="text-xl font-semibold mb-3">About this place</h2>
            <p className="text-gray-600 leading-relaxed">
              Wake up to the sound of waves in this stunning beach house. 
              Perfect for families or groups seeking a peaceful coastal retreat. 
              The property features modern amenities, a fully equipped kitchen, 
              and direct beach access.
            </p>
          </div>

          {/* Amenities */}
          <div className="border-b border-gray-200 pb-6">
            <h2 className="text-xl font-semibold mb-3">What this place offers</h2>
            <div className="grid grid-cols-2 gap-4">
              <div className="flex items-center gap-3">
                <span>🏖️</span>
                <span>Beach access</span>
              </div>
              <div className="flex items-center gap-3">
                <span>📶</span>
                <span>WiFi</span>
              </div>
              <div className="flex items-center gap-3">
                <span>🅿️</span>
                <span>Free parking</span>
              </div>
              <div className="flex items-center gap-3">
                <span>🍳</span>
                <span>Kitchen</span>
              </div>
              <div className="flex items-center gap-3">
                <span>❄️</span>
                <span>Air conditioning</span>
              </div>
              <div className="flex items-center gap-3">
                <span>🧺</span>
                <span>Washer</span>
              </div>
            </div>
          </div>
        </div>

        {/* Right Column - Booking Card */}
        <div className="lg:col-span-1">
          <div className="border border-gray-200 rounded-lg p-6 shadow-md sticky top-4">
            <div className="mb-4">
              <span className="text-2xl font-bold">$250</span>
              <span className="text-gray-600"> / night</span>
            </div>

            <div className="mb-4">
              <div className="text-sm text-gray-600 mb-2">
                ⭐ 4.8 · 124 reviews
              </div>
            </div>

            <button className="w-full bg-pink-600 hover:bg-pink-700 text-white font-semibold py-3 px-6 rounded-lg transition-colors">
              Check Availability
            </button>

            <p className="text-center text-sm text-gray-600 mt-4">
              You won't be charged yet
            </p>
          </div>
        </div>
      </div>
    </div>
  );
}

Viewing the Page

Right now, you can't navigate to this page yet because we haven't added a route! But let's verify the component is working:

Temporarily Test the Page

You can temporarily import and render it in App.jsx to see it:

src/App.jsx (Temporary - Don't Save)
import ListingDetailsPage from './pages/ListingDetailsPage';

function App() {
  return <ListingDetailsPage />;
}

export default App;

Check the browser - You should see the details page layout!

Revert the Change

After viewing, change App.jsx back:

src/App.jsx
import Router from './components/Router';

function App() {
  return <Router />;
}

export default App;

In the next lesson, we'll add the route properly so you can navigate to it!

Responsive Design

Let's see how the layout adapts to different screens:

On phones (< 1024px):

┌─────────────────┐
│  Header         │
├─────────────────┤
│  Images         │
├─────────────────┤
│  Main Content   │
│  - Description  │
│  - Amenities    │
├─────────────────┤
│  Booking Card   │
└─────────────────┘

Everything stacks vertically (grid-cols-1).

On tablets (1024px):

Starts transitioning to two-column layout.

On desktops (> 1024px):

┌─────────────────────────────────────┐
│  Header                             │
├─────────────────────────────────────┤
│  Images                             │
├─────────────────┬───────────────────┤
│  Main Content   │  Booking Card     │
│  (2/3 width)    │  (1/3 width)      │
│  - Description  │  - Sticky         │
│  - Amenities    │                   │
│  - Reviews      │                   │
└─────────────────┴───────────────────┘

Two-column grid (lg:grid-cols-3 with span-2 and span-1).

Future Enhancements

In upcoming lessons, we'll enhance this page with:

Lesson 5-6: Dynamic routing to show different listings
Lesson 7: Fetch real listing data from API
Lesson 8: Create a proper ListingDetailsCard component
Lesson 9: Add navigation from HomePage to this page

For now, we have a solid foundation!

Page Component Best Practices

Page Component Created!

You've built the ListingDetailsPage component with a professional layout! It has header, images, content sections, and a sticky booking sidebar. In the next lesson, we'll connect it to a route with dynamic URL parameters.

Quick Recap

What we accomplished:

  • ✅ Created ListingDetailsPage.jsx in pages folder
  • ✅ Built a two-column responsive layout
  • ✅ Added header, content sections, and booking card
  • ✅ Used Tailwind grid for responsive design
  • ✅ Made booking card sticky on scroll
  • ✅ Added placeholder content and structure

Key concepts:

  • Page components - Represent full routes/screens
  • Grid layout - Two-column layout with grid-cols and col-span
  • Sticky positioning - Keep elements visible while scrolling
  • Responsive design - Different layouts for mobile/desktop
  • Semantic structure - Organize content logically

What's Next?

In Lesson 5, we'll add a dynamic route with a URL parameter (:id) so each listing can have its own unique URL like /listing/1, /listing/2, etc. This is where routing gets really powerful! 🚀