We use cookies to enhance your experience on the site
CodeWorlds

URL Parameters in Cosmic Navigation

In interplanetary travels, coordinates and identifiers of celestial bodies are key to navigation. Similarly in React Router, URL parameters allow for precisely directing the user to specific application resources. In this module, we'll learn how to use, read, and manipulate URL parameters in our space applications.

What are URL Parameters?

URL parameters are variable parts of a URL address that can contain dynamic values. In React Router, they come in two main forms:

  1. Path parameters - embedded in the URL structure (e.g.,
    /planets/:planetId
    )
  2. Query parameters - added after the question mark (e.g.,
    /planets?type=rocky&inhabited=true
    )

Path Parameters

Path parameters are part of the URL structure itself and are defined by a colon (

:
) in the route definition.

Defining Routes with Path Parameters

1import { Routes, Route } from 'react-router-dom';
2
3function AppRoutes() {
4  return (
5    <Routes>
6      <Route path="/" element={<Dashboard />} />
7      <Route path="/planets/:planetId" element={<PlanetDetails />} />
8      <Route path="/missions/:year/:missionId" element={<MissionDetails />} />
9    </Routes>
10  );
11}

In the example above:

  • :planetId
    is a parameter in the path
    /planets/:planetId
  • :year
    and
    :missionId
    are parameters in the path
    /missions/:year/:missionId

These paths will match URLs such as:

  • /planets/mars
  • /planets/jupiter
  • /missions/2150/mars-expedition

Accessing Path Parameters with useParams

To read path parameter values in a component, we use the

useParams
hook:

1import { useParams } from 'react-router-dom';
2
3function PlanetDetails() {
4  const { planetId } = useParams();
5
6  return (
7    <div className="planet-details">
8      <h1>Planet details: {planetId}</h1>
9      {/* Rest of the component using the planetId value */}
10    </div>
11  );
12}

In a more complex component, we can use the parameter to fetch data:

1import { useParams } from 'react-router-dom';
2import { useState, useEffect } from 'react';
3
4function PlanetDetails() {
5  const { planetId } = useParams();
6  const [planetData, setPlanetData] = useState(null);
7  const [loading, setLoading] = useState(true);
8  const [error, setError] = useState(null);
9
10  useEffect(() => {
11    // Reset state when parameter changes
12    setLoading(true);
13    setError(null);
14
15    // Fetch data for the specific planet
16    fetch(`https://api.space-explorer.com/planets/${planetId}`)
17      .then(response => {
18        if (!response.ok) {
19          throw new Error('Unable to fetch planet data');
20        }
21        return response.json();
22      })
23      .then(data => {
24        setPlanetData(data);
25        setLoading(false);
26      })
27      .catch(err => {
28        setError(err.message);
29        setLoading(false);
30      });
31  }, [planetId]); // Re-run the effect when planetId changes
32
33  if (loading) return <div>Loading planet data...</div>;
34  if (error) return <div>Error: {error}</div>;
35  if (!planetData) return <div>Planet not found</div>;
36
37  return (
38    <div className="planet-details">
39      <h1>{planetData.name}</h1>
40      <div className="planet-info">
41        <p>Diameter: {planetData.diameter} km</p>
42        <p>Distance from the Sun: {planetData.distanceFromSun} million km</p>
43        <p>Surface temperature: {planetData.surfaceTemperature}°C</p>
44        <p>Type: {planetData.type}</p>
45      </div>
46      {/* More information about the planet */}
47    </div>
48  );
49}

Optional Path Parameters

In React Router v6, there is no direct way to define optional path parameters. Instead, we can use multiple routes:

1<Routes>
2  <Route path="/missions" element={<MissionsList />} />
3  <Route path="/missions/:year" element={<MissionsList />} />
4</Routes>

Or use query parameters for optional values.

Query Parameters

Query parameters are key-value pairs added after the question mark (

?
) in the URL. Example:
/search?query=mars&category=planets

Accessing Query Parameters with useSearchParams

The

useSearchParams
hook allows reading and manipulating query parameters:

1import { useSearchParams } from 'react-router-dom';
2
3function PlanetSearch() {
4  // searchParams is a URLSearchParams object
5  // setSearchParams is a function to update the parameters
6  const [searchParams, setSearchParams] = useSearchParams();
7
8  // Reading parameter values
9  const query = searchParams.get('query') || '';
10  const type = searchParams.get('type') || 'all';
11
12  // Function handling the search
13  const handleSearch = (e) => {
14    e.preventDefault();
15    const formData = new FormData(e.target);
16    const newQuery = formData.get('searchQuery');
17    const newType = formData.get('planetType');
18
19    // Update query parameters
20    setSearchParams({ query: newQuery, type: newType });
21  };
22
23  return (
24    <div className="planet-search">
25      <h1>Planet Search Engine</h1>
26
27      <form onSubmit={handleSearch}>
28        <input
29          name="searchQuery"
30          defaultValue={query}
31          placeholder="Enter planet name..."
32        />
33
34        <select name="planetType" defaultValue={type}>
35          <option value="all">All types</option>
36          <option value="rocky">Rocky</option>
37          <option value="gas-giant">Gas giants</option>
38          <option value="ice-giant">Ice giants</option>
39        </select>
40
41        <button type="submit">Search</button>
42      </form>
43
44      <div className="search-results">
45        {/* Search results depending on query and type */}
46        <p>Search phrase: {query || '(none)'}</p>
47        <p>Planet type: {type}</p>
48      </div>
49    </div>
50  );
51}

Manipulating Query Parameters

We can manipulate query parameters in different ways:

1// Set new parameters (replaces all existing ones)
2setSearchParams({ query: 'mars', type: 'rocky' });
3
4// Add or update a parameter without removing existing ones
5setSearchParams(params => {
6  params.set('query', 'mars');
7  return params;
8});
9
10// Remove a parameter
11setSearchParams(params => {
12  params.delete('type');
13  return params;
14});
15
16// Check if a parameter exists
17const hasType = searchParams.has('type');
18
19// Get all values for a parameter (if it occurs multiple times)
20const allCategories = searchParams.getAll('category');

Query Parameters vs. Path Parameters - When to Use Which?

Path Parameters:

  • For identifying the main resource (e.g.,
    /planets/mars
    )
  • When the parameter is required
  • When the URL structure should reflect the resource hierarchy
  • Examples: planet ID, mission year, constellation name

Query Parameters:

  • For filtering, sorting, pagination
  • For optional parameters
  • When there are many parameter combinations
  • Examples: search criteria, results page, limits, filters

Combining Path and Query Parameters

We can combine both types of parameters for greater flexibility:

1// URL: /planets/mars?tab=geology&unit=metric
2
3import { useParams, useSearchParams } from 'react-router-dom';
4
5function PlanetDetails() {
6  const { planetId } = useParams();
7  const [searchParams] = useSearchParams();
8
9  const activeTab = searchParams.get('tab') || 'overview';
10  const unit = searchParams.get('unit') || 'metric';
11
12  return (
13    <div>
14      <h1>Planet: {planetId}</h1>
15
16      <div className="tabs">
17        <TabLink to={`/planets/${planetId}?tab=overview&unit=${unit}`}
18          active={activeTab === 'overview'}>
19          Overview
20        </TabLink>
21        <TabLink to={`/planets/${planetId}?tab=geology&unit=${unit}`}
22          active={activeTab === 'geology'}>
23          Geology
24        </TabLink>
25        <TabLink to={`/planets/${planetId}?tab=atmosphere&unit=${unit}`}
26          active={activeTab === 'atmosphere'}>
27          Atmosphere
28        </TabLink>
29      </div>
30
31      <div className="unit-switcher">
32        <UnitToggle
33          metric={unit === 'metric'}
34          onChange={(isMetric) => {
35            const newUnit = isMetric ? 'metric' : 'imperial';
36            // Update only the unit parameter, preserve tab
37            searchParams.set('unit', newUnit);
38            setSearchParams(searchParams);
39          }}
40        />
41      </div>
42
43      {/* Content depending on activeTab and unit */}
44    </div>
45  );
46}

createSearchParams - Simplifying Query Parameter Creation

The

createSearchParams
function helps create query parameters from JavaScript objects:

1import { createSearchParams, useNavigate } from 'react-router-dom';
2
3function PlanetFilterPanel() {
4  const navigate = useNavigate();
5
6  const applyFilters = (filters) => {
7    // Convert filter object to URL parameters
8    const params = createSearchParams({
9      type: filters.type,
10      minSize: filters.minSize,
11      hasWater: filters.hasWater ? 'true' : undefined,
12      sortBy: filters.sortBy
13    });
14
15    // Navigate with parameters
16    navigate({
17      pathname: '/planets',
18      search: `?${params}`
19    });
20  };
21
22  return (
23    <form onSubmit={(e) => {
24      e.preventDefault();
25      const form = e.target;
26      applyFilters({
27        type: form.type.value,
28        minSize: form.minSize.value,
29        hasWater: form.hasWater.checked,
30        sortBy: form.sortBy.value
31      });
32    }}>
33      {/* Filter form */}
34      <button type="submit">Apply filters</button>
35    </form>
36  );
37}

Generating Links with Parameters

When creating links with parameters, we can use:

For path parameters:

1// Simple string concatenation
2<Link to={`/planets/${planetId}`}>
3  Planet details {planetName}
4</Link>
5
6// Using an object (more flexible)
7<Link to={{
8  pathname: `/planets/${planetId}`,
9}}>
10  Planet details {planetName}
11</Link>

For query parameters:

1import { Link, createSearchParams } from 'react-router-dom';
2
3// Using createSearchParams
4<Link to={{
5  pathname: '/planets',
6  search: `?${createSearchParams({
7    type: 'rocky',
8    inhabited: 'true'
9  })}`
10}}>
11  Inhabited rocky planets
12</Link>
13
14// Or manually creating parameters
15<Link to={`/planets?type=rocky&inhabited=true`}>
16  Inhabited rocky planets
17</Link>

Full Example Application with URL Parameters

Let's now see a complete space application example using both path parameters and query parameters:

1import React, { useState, useEffect } from 'react';
2import {
3  BrowserRouter,
4  Routes,
5  Route,
6  Link,
7  useParams,
8  useSearchParams,
9  createSearchParams,
10  useNavigate
11} from 'react-router-dom';
12
13// API simulation
14const planetsData = [
15  { id: 'mercury', name: 'Mercury', type: 'rocky', diameter: 4879, hasWater: false },
16  { id: 'venus', name: 'Venus', type: 'rocky', diameter: 12104, hasWater: false },
17  { id: 'earth', name: 'Earth', type: 'rocky', diameter: 12742, hasWater: true },
18  { id: 'mars', name: 'Mars', type: 'rocky', diameter: 6779, hasWater: true },
19  { id: 'jupiter', name: 'Jupiter', type: 'gas-giant', diameter: 139820, hasWater: true },
20  { id: 'saturn', name: 'Saturn', type: 'gas-giant', diameter: 116460, hasWater: true },
21  { id: 'uranus', name: 'Uranus', type: 'ice-giant', diameter: 50724, hasWater: true },
22  { id: 'neptune', name: 'Neptune', type: 'ice-giant', diameter: 49244, hasWater: true },
23];
24
25// Main components
26function PlanetsList() {
27  const [searchParams, setSearchParams] = useSearchParams();
28  const navigate = useNavigate();
29
30  // Read query parameters
31  const type = searchParams.get('type') || 'all';
32  const hasWater = searchParams.get('hasWater');
33  const sortBy = searchParams.get('sortBy') || 'name';
34
35  // Filter planets
36  const filteredPlanets = planetsData
37    .filter(planet => type === 'all' || planet.type === type)
38    .filter(planet => hasWater === null || (hasWater === 'true' && planet.hasWater))
39    .sort((a, b) => {
40      if (sortBy === 'name') return a.name.localeCompare(b.name);
41      if (sortBy === 'diameter') return b.diameter - a.diameter;
42      return 0;
43    });
44
45  // Function to update filters
46  const updateFilters = (newFilters) => {
47    const params = createSearchParams({
48      ...Object.fromEntries(searchParams),
49      ...newFilters
50    });
51    setSearchParams(params);
52  };
53
54  return (
55    <div className="planets-list">
56      <h1>Planet Catalog</h1>
57
58      <div className="filters">
59        <label>
60          Planet type:
61          <select
62            value={type}
63            onChange={(e) => updateFilters({ type: e.target.value })}
64          >
65            <option value="all">All</option>
66            <option value="rocky">Rocky</option>
67            <option value="gas-giant">Gas giants</option>
68            <option value="ice-giant">Ice giants</option>
69          </select>
70        </label>
71
72        <label>
73          <input
74            type="checkbox"
75            checked={hasWater === 'true'}
76            onChange={(e) => updateFilters({ hasWater: e.target.checked ? 'true' : null })}
77          />
78          With water only
79        </label>
80
81        <label>
82          Sort by:
83          <select
84            value={sortBy}
85            onChange={(e) => updateFilters({ sortBy: e.target.value })}
86          >
87            <option value="name">Name</option>
88            <option value="diameter">Size</option>
89          </select>
90        </label>
91      </div>
92
93      <ul className="planets-grid">
94        {filteredPlanets.map(planet => (
95          <li key={planet.id} className={`planet ${planet.type}`}>
96            <Link to={`/planets/${planet.id}`}>
97              <h2>{planet.name}</h2>
98              <p>Type: {planet.type}</p>
99              <p>Diameter: {planet.diameter} km</p>
100              <p>Water: {planet.hasWater ? 'Present' : 'Absent'}</p>
101            </Link>
102          </li>
103        ))}
104      </ul>
105
106      {filteredPlanets.length === 0 && (
107        <p>No planets found matching the criteria.</p>
108      )}
109
110      <button onClick={() => navigate('/')}>
111        Return to control center
112      </button>
113    </div>
114  );
115}
116
117function PlanetDetails() {
118  const { planetId } = useParams();
119  const [searchParams, setSearchParams] = useSearchParams();
120  const navigate = useNavigate();
121
122  // Read query parameters
123  const activeTab = searchParams.get('tab') || 'overview';
124
125  // Find planet with given ID
126  const planet = planetsData.find(p => p.id === planetId);
127
128  // If planet not found
129  if (!planet) {
130    return (
131      <div className="planet-not-found">
132        <h1>Planet does not exist!</h1>
133        <p>No planet found with identifier: {planetId}</p>
134        <button onClick={() => navigate('/planets')}>
135          Return to planet catalog
136        </button>
137      </div>
138    );
139  }
140
141  // Change active tab
142  const changeTab = (tab) => {
143    setSearchParams({ tab });
144  };
145
146  return (
147    <div className="planet-details">
148      <h1>{planet.name}</h1>
149
150      <div className="tabs">
151        <button
152          className={activeTab === 'overview' ? 'active' : ''}
153          onClick={() => changeTab('overview')}
154        >
155          Overview
156        </button>
157        <button
158          className={activeTab === 'composition' ? 'active' : ''}
159          onClick={() => changeTab('composition')}
160        >
161          Composition
162        </button>
163        <button
164          className={activeTab === 'exploration' ? 'active' : ''}
165          onClick={() => changeTab('exploration')}
166        >
167          Exploration
168        </button>
169      </div>
170
171      <div className="tab-content">
172        {activeTab === 'overview' && (
173          <div>
174            <h2>Overview of planet {planet.name}</h2>
175            <p>Type: {planet.type}</p>
176            <p>Diameter: {planet.diameter} km</p>
177            <p>Water: {planet.hasWater ? 'Present' : 'Absent'}</p>
178          </div>
179        )}
180
181        {activeTab === 'composition' && (
182          <div>
183            <h2>Composition of planet {planet.name}</h2>
184            <p>Information about planet composition will appear here...</p>
185          </div>
186        )}
187
188        {activeTab === 'exploration' && (
189          <div>
190            <h2>Exploration of planet {planet.name}</h2>
191            <p>Information about exploration missions will appear here...</p>
192          </div>
193        )}
194      </div>
195
196      <Link to="/planets">Return to planet catalog</Link>
197    </div>
198  );
199}
200
201function App() {
202  return (
203    <BrowserRouter>
204      <div className="space-app">
205        <header>
206          <h1>Cosmic Navigation System</h1>
207        </header>
208
209        <main>
210          <Routes>
211            <Route path="/" element={<Dashboard />} />
212            <Route path="/planets" element={<PlanetsList />} />
213            <Route path="/planets/:planetId" element={<PlanetDetails />} />
214            <Route path="*" element={<NotFound />} />
215          </Routes>
216        </main>
217
218        <footer>
219          <p>&copy; 2150 Interplanetary Space Agency</p>
220        </footer>
221      </div>
222    </BrowserRouter>
223  );
224}
225
226// Other components
227function Dashboard() {
228  return (
229    <div className="dashboard">
230      <h1>Mission Control Center</h1>
231      <p>Welcome to the cosmic navigation system!</p>
232      <Link to="/planets">Browse planet catalog</Link>
233    </div>
234  );
235}
236
237function NotFound() {
238  return (
239    <div className="not-found">
240      <h1>Navigation Error: 404</h1>
241      <p>Lost in space! This page does not exist.</p>
242      <Link to="/">Return to Control Center</Link>
243    </div>
244  );
245}
246
247export default App;

Best Practices for URL Parameters

  1. Use readable parameter names - they should describe what they represent
  2. Choose the appropriate type of parameters - path parameters for main resources, query parameters for filters
  3. Handle missing parameters - always provide default values
  4. Validate parameters - check if values are correct before using them
  5. Preserve query parameter state - update only the parameters that change
  6. Use parameters to store UI state - this makes it easier to share and save state

Summary

URL parameters are a powerful tool in cosmic navigation (and React Router). They allow for:

  • Identifying specific resources (path parameters)
  • Filtering and sorting data (query parameters)
  • Remembering the user interface state
  • Creating shareable and bookmarkable links
  • Handling browser history

Thanks to them, users can precisely navigate to specific locations in the space application, and you as a programmer can create interactive and user-friendly interfaces.

Go to CodeWorlds