React Router Nesting Challenge

This challenge focuses on creating nested routes using React Router, including dynamic segments that enable you to show details for a specific item (like a movie). You will use useParams to capture route parameters from the URL, and you'll nest routes so your UI can display multiple levels of content.

By the end, you will be able to:

Devising a Plan

  1. Pass data via props:
  2. Set up a nested route:
  3. Create a navigation bar for movies:
  4. Use dynamic route parameters:

Carrying Out the Plan

Below is a basic implementation. We will assume a minimal folder structure. Notice how we avoid spaces or hyphens in filenames, using underscores instead:

my_react_app/
  package.json
  src/
    index.js
    app.jsx
    router.jsx
    data/
      movie_data.js
    components/
      layout.jsx
      movies/
        movies.jsx
        movie_details.jsx
        movie_nav_bar.jsx
    ...
  public/
    index.html
  ...

Follow these file examples and adapt to your own project setup. Each code snippet below includes pseudocode comments, expected input, and expected output for clarity.

File: src/data/movie_data.js

// Basic movie data array
// Expected Input: none, this file just exports data
// Expected Output: An array of movie objects with id, title, and description

export const movies = [
  {
    id: 1,
    title: 'The Informer',
    description: 'A suspenseful crime drama with unexpected twists.'
  },
  {
    id: 2,
    title: 'Lost in Space',
    description: 'A sci-fi family adventure beyond the stars.'
  },
  {
    id: 3,
    title: 'Treasure Island',
    description: 'A classic tale of pirates and hidden gold.'
  }
];

File: src/components/movies/movie_details.jsx

// MovieDetails component using useParams
// Expected Input: a 'movies' prop array, plus a URL parameter for movieId
// Expected Output: displays the details (title, description) of the selected movie

import React from 'react';
import { useParams } from 'react-router-dom';

function Movie_Details({ movies }) {
  // <-- Pseudocode: we extract the movieId from the URL via useParams
  const { movieId } = useParams();
  console.log('Movie ID from URL:', movieId); // for debugging

  // <-- Convert the movieId to a number for accurate comparison
  const idNumber = parseInt(movieId, 10);

  // <-- Find the movie that matches the id
  const movieChoice = movies.find((movie) => movie.id === idNumber);

  // <-- If not found, render a 'Movie not found' message
  if (!movieChoice) {
    return <h2>Movie not found</h2>;
  }

  return (
    <div>
      <h1>{movieChoice.title}</h1>
      <p>{movieChoice.description}</p>
    </div>
  );
}

export default Movie_Details;

File: src/components/movies/movie_nav_bar.jsx

// MovieNavBar component
// Expected Input: a 'movies' prop (array of movie objects)
// Expected Output: a nav of links for each movie (links to /movies/:movieId)

import React from 'react';
import { NavLink } from 'react-router-dom';

function Movie_Nav_Bar({ movies }) {
  // <-- Pseudocode: create a nav bar for each movie
  return (
    <nav>
      <ul>
        {movies.map((movie) => (
          <li key={movie.id}>
            <NavLink 
              to={String(movie.id)} 
              style={({ isActive }) => ({
                fontWeight: isActive ? 'bold' : 'normal'
              })}
            >
              {movie.title}
            </NavLink>
          </li>
        ))}
      </ul>
    </nav>
  );
}

export default Movie_Nav_Bar;

File: src/components/movies/movies.jsx

// Movies component with nested Outlet
// Expected Input: a 'movies' prop (array of movie objects)
// Expected Output: Renders the main 'Movies Component' text, a nav bar, and the child route

import React from 'react';
import { Outlet } from 'react-router-dom';
import Movie_Nav_Bar from './movie_nav_bar';

function Movies({ movies }) {
  // <-- Pseudocode: Display a heading and a nav bar for movie links
  // Then use <Outlet /> for any nested route to show details
  return (
    <div>
      <h1>Movies Component</h1>
      <Movie_Nav_Bar movies={movies} />

      <!-- Child route content (MovieDetails) will render here -->
      <Outlet />
    </div>
  );
}

export default Movies;

File: src/app.jsx

// App component hooking up the router
// Expected Input: none, but provides the entire app
// Expected Output: the RouterProvider with all the routes
// We'll import 'movies' data here and pass to routes

import React from 'react';
import { RouterProvider } from 'react-router-dom';
import router from './router';

function App() {
  // <-- Pseudocode: Provide the router to the entire app
  return <RouterProvider router={router} />;
}

export default App;

File: src/router.jsx

// Router Setup with nested routes
// We pass 'movies' to the 'Movies' and 'Movie_Details' components

import React from 'react';
import { createBrowserRouter } from 'react-router-dom';

// Layout and pages
import Layout from './components/layout';
import Movies from './components/movies/movies';
import Movie_Details from './components/movies/movie_details';

// Other pages:
import Home from './components/home';
import Stocks from './components/stocks';
import { movies } from './data/movie_data';

const router = createBrowserRouter([
  {
    element: <Layout />,
    children: [
      {
        path: '/',
        element: <Home />
      },
      {
        path: 'stocks',
        element: <Stocks />
      },
      {
        path: 'movies',
        element: <Movies movies={movies} />,
        // Nested route for individual movie details
        children: [
          {
            path: ':movieId',
            element: <Movie_Details movies={movies} />
          }
        ]
      },
      {
        path: '*',
        element: <h1>Page Not Found</h1>
      }
    ]
  }
]);

export default router;

File: src/components/layout.jsx

// Layout component for a global nav or structure
// Expected Input: none
// Expected Output: Renders a top-level layout with a nav plus an <Outlet /> for child routes

import React from 'react';
import { Outlet, NavLink } from 'react-router-dom';

function Layout() {
  return (
    <div className='app'>
      <h1>App Component</h1>

      <nav className="comp nav">
        <ul>
          <li>
            <NavLink 
              to="/" 
              style={({ isActive }) => ({
                fontWeight: isActive ? 'bold' : 'normal'
              })}
            >
              Home
            </NavLink>
          </li>
          <li>
            <NavLink 
              to="/stocks" 
              style={({ isActive }) => ({
                fontWeight: isActive ? 'bold' : 'normal'
              })}
            >
              Stocks
            </NavLink>
          </li>
          <li>
            <NavLink
              to="/movies"
              style={({ isActive }) => ({
                fontWeight: isActive ? 'bold' : 'normal'
              })}
            >
              Movies
            </NavLink>
          </li>
        </ul>
      </nav>

      <main>
        <Outlet /> 
      </main>
    </div>
  );
}

export default Layout;

File: src/components/home.jsx

// Very simple Home component
// Expected Input: none
// Expected Output: Renders a welcome message

import React from 'react';

function Home() {
  return <h2>Welcome to the Home Page!</h2>;
}

export default Home;

File: src/components/stocks.jsx

// Simple Stocks component
// Expected Input: none
// Expected Output: Renders "Stocks Component"

import React from 'react';

function Stocks() {
  return <h2>Stocks Component</h2>;
}

export default Stocks;

After placing these files, run your dev server (npm run dev or npm start for some setups). Navigate to:

You should see the nested route display the Movie_Details component within the Movies component.

Basic Elementary Solution: The above code is minimal, demonstrating the core idea of nested routes, dynamic segments, and a simple usage of useParams.

More Advanced Adjustments:

Reflecting on the Solution

Think of nesting like a bookstore: the main “Movies” section is one big category, but each movie inside has its own dedicated “aisle” for details. useParams is your sign telling you which aisle the user selected, allowing you to fetch or filter the relevant data from your broader “catalog.”

Practical Applications:

This approach makes your code more organized and your user experience more intuitive.

Enjoy exploring nested routes in React Router, and feel free to add additional layers to practice advanced nesting, data fetching, and route guards.