URL Parameters and Search
URL Parameters and Search
A teacher wants to share Maya’s attendance view with another teacher. A generic /students address cannot identify which student or which view should open. The address needs enough public screen information to reproduce that choice.
A route parameter captures a variable path segment. A search parameter is a key-value item after the URL’s question mark. Both are external input: their presence does not prove the requested record exists.
Read an ID
Capture the student path segment.
Choose a view
Read and update a search parameter.
Validate input
Handle unknown IDs and unsupported views deliberately.
Follow information through the application
Path
/students/007 identifies a requested student.
Search
tab=summary selects a view.
Lookup
The application validates both before rendering.
Give the path and query different jobs
Use /students/:studentId when the screen identifies one student. The colon marks a variable segment in the route definition, not in the actual address. useParams returns the matched studentId as text. Keeping it as text preserves leading zeroes: identifier 007 is not automatically the number seven.
Use ?tab=attendance for a view choice that belongs to that student page. useSearchParams returns the current URLSearchParams object and a setter that navigates to the changed search. The useParams reference and the useSearchParams reference describe these separate sources of URL information.
Read a requested student and view
The complete React Router example starts with Maya’s summary view in an isolated memory history. Show attendance updates the search parameters; selecting Ravi changes the student ID and resets the link’s tab to summary. The list contains both records, so both links have known lookup targets.
The view expression accepts only attendance explicitly and falls back to summary otherwise. This is a small allowed-value policy. For an unknown student, the component displays Student not found before using a missing record’s name.
React · JavaScript with JSX
import React from 'react';
import { MemoryRouter, Routes, Route, Link, useParams, useSearchParams } from 'react-router';
const students = [{ id: '007', name: 'Maya' }, { id: '009', name: 'Ravi' }];
function Student() {
const { studentId } = useParams();
const [search, setSearch] = useSearchParams();
const student = students.find(item => item.id === studentId);
if (!student) return <h2>Student not found</h2>;
const tab = search.get('tab') === 'attendance' ? 'attendance' : 'summary';
return <section><h2>{student.name}</h2><p>View: {tab}</p>
<button onClick={() => setSearch({ tab: 'attendance' })}>Show attendance</button>
</section>;
}
export default function App() {
return <MemoryRouter initialEntries={['/students/007?tab=summary']}>
<Link to="/students/009?tab=summary">Ravi</Link>
<Routes><Route path="/students/:studentId" element={<Student />} />
<Route path="*" element={<h2>Page not found</h2>} /></Routes>
</MemoryRouter>;
}Expected screen and behavior
Initially: Ravi link, Maya heading, View: summary and Show attendance.
Show attendance changes the view label; Ravi opens Ravi’s summary.URL changes cause a new render
React · the URL-reading component
import React from 'react';
import { MemoryRouter, Routes, Route, Link, useParams, useSearchParams } from 'react-router';
const students = [{ id: '007', name: 'Maya' }, { id: '009', name: 'Ravi' }];
function Student() {
const { studentId } = useParams();
const [search, setSearch] = useSearchParams();
const student = students.find(item => item.id === studentId);
if (!student) return <h2>Student not found</h2>;
const tab = search.get('tab') === 'attendance' ? 'attendance' : 'summary';
return <section><h2>{student.name}</h2><p>View: {tab}</p>
<button onClick={() => setSearch({ tab: 'attendance' })}>Show attendance</button>
</section>;
}
export default function App() {
return <MemoryRouter initialEntries={['/students/007?tab=summary']}>
<Link to="/students/009?tab=summary">Ravi</Link>
<Routes><Route path="/students/:studentId" element={<Student />} />
<Route path="*" element={<h2>Page not found</h2>} /></Routes>
</MemoryRouter>;
}Student reads location information from the surrounding router, then performs a lookup and derives the displayed tab. It does not copy studentId into state. When the location changes, the router supplies new values and React renders the resulting screen. Copying these values into independent state would create two owners that could disagree.
The known student array is local demonstration data, not a fetched school database. A real page may need loading, access-denied and missing-record states. The path is a request for a record, not evidence that the viewer is allowed to read it.
Preserve unrelated search choices when needed
setSearch({ tab: "attendance" }) replaces this example’s search information. That is suitable because tab is its only query setting. In a page that also has a sort setting, create a copy and update only tab instead of accidentally discarding sort. The following event-handler fragment belongs inside Student; it is not a complete application by itself.
function showAttendance() {
setSearch(previous => {
const next = new URLSearchParams(previous);
next.set('tab', 'attendance');
return next;
});
}Treat a URL change as navigation. Decide whether users should return to every change with Back or whether a replace-style update fits a frequently changing filter. Avoid putting confidential marks, access tokens or unsaved private comments into shareable query strings.
Worked practice: handle an unknown identifier
Change the initial address to /students/011?tab=attendance. Predict the visible heading before examining the answer. The complete worked version keeps the same route and lookup rules.
Student not found appears because no record has ID 011. Matching the route pattern only supplies an identifier; it cannot manufacture a student. Returning early keeps the program from trying to render student.name when student is undefined.
React · JavaScript with JSX
import React from 'react';
import { MemoryRouter, Routes, Route, Link, useParams, useSearchParams } from 'react-router';
const students = [{ id: '007', name: 'Maya' }, { id: '009', name: 'Ravi' }];
function Student() {
const { studentId } = useParams();
const [search, setSearch] = useSearchParams();
const student = students.find(item => item.id === studentId);
if (!student) return <h2>Student not found</h2>;
const tab = search.get('tab') === 'attendance' ? 'attendance' : 'summary';
return <section><h2>{student.name}</h2><p>View: {tab}</p>
<button onClick={() => setSearch({ tab: 'attendance' })}>Show attendance</button>
</section>;
}
export default function App() {
return <MemoryRouter initialEntries={['/students/011?tab=attendance']}>
<Link to="/students/009?tab=summary">Ravi</Link>
<Routes><Route path="/students/:studentId" element={<Student />} />
<Route path="*" element={<h2>Page not found</h2>} /></Routes>
</MemoryRouter>;
}Expected screen and behavior
Initially: Ravi link and Student not found.Validate external input and define ownership
Search parameter values are strings or absent values, not validated booleans or numbers. Read the expected key, check the allowed values, and choose an explicit default. Do not assume ?tab=false is boolean false or that every ID may safely become a number.
Keep shareable selection or filtering in the URL when that is useful. Keep an unfinished form draft in component state unless the product explicitly calls for a different policy. With BrowserRouter, direct student URLs still require the host behavior explained in client-side routing.
Check the identifier
Apply a query update
Make useful URLs with clear defaults
Path parameters identify requested resources; search parameters express additional page choices. Read them through the router, validate their values, handle missing records and avoid duplicated state. Updating a query is navigation, so preserve unrelated choices intentionally.
Connect this with choosing one state owner and preventing stale data when selection changes.
Reaching the recap and completing the quizzes are separate milestones.