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.
URL parameters are variable parts of a URL address that can contain dynamic values. In React Router, they come in two main forms:
/planets/:planetId)/planets?type=rocky&inhabited=true)Path parameters are part of the URL structure itself and are defined by a colon (
:) in the route definition.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/:missionIdThese paths will match URLs such as:
/planets/mars/planets/jupiter/missions/2150/mars-expeditionTo 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}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 are key-value pairs added after the question mark (
?) in the URL.
Example: /search?query=mars&category=planetsThe
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}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');/planets/mars)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}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}When creating links with parameters, we can use:
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>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>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>© 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;URL parameters are a powerful tool in cosmic navigation (and React Router). They allow for:
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.