URL State Management
NEVER use raw useSearchParams — always use the dedicated hooks from frontend/src/hooks/url-state/.
Available Hooks
The four core hooks:
| Hook | Purpose | Example |
|---|---|---|
useTabState(validTabs, default) | Tab switching with URL sync | ?tab=details |
useFilterState(defaults) | Multi-param filter state | ?status=active&search=solar |
useModalState(param?) | Deep-linkable modal/dialog | ?modal=create |
useMultiSortState(columns, defaultSort) | Multi-column sort with Shift+Click stacking | ?sort=name.asc,date.desc |
Additional hooks (as of 2026-06): useBillingFilterState (consolidated URL contract for the billing tabs — period, filters, open invoice, form mode), useSortWeightsState (Kanban sort weights encoded in one URL param, history-replace), and useUrlBatchUpdate (atomic multi-key search-param update — multiple setSearchParams calls in one handler race and silently lose updates).
All hooks are exported from frontend/src/hooks/url-state/index.ts.
Benefits
- Shareable URLs — copy/paste a filtered view to a colleague
- Browser navigation — back/forward buttons work with state changes
- Refresh survival — state persists across page reloads
- Bookmarkable — save specific filtered/sorted views
Usage Examples
// Tab management
const [activeTab, setActiveTab] = useTabState(['overview', 'details', 'files'], 'overview');
// Filter state with defaults
const [filters, setFilters] = useFilterState({
status: 'all',
search: '',
});Multi-Sort
useMultiSortState supports stacking sort columns via Shift+Click:
const [sort, toggleSort] = useMultiSortState(
['name', 'date', 'status'],
[{ column: 'date', direction: 'desc' }]
);Single click replaces the sort. Shift+Click adds a secondary sort column.
Testing
Legacy tests live in frontend/src/hooks/url-state/__tests__/ (useTabState, useFilterState, useModalState); newer tests are co-located with the .unit.test.tsx suffix (e.g. useBillingFilterState.unit.test.tsx) per the PR#1690 convention.
See Also
- State Management — when to use URL state vs alternatives
- Hooks — overall hook organization
- Frontend Architecture — routing and URL structure
- Component Decomposition — where URL hooks are called
- React Query Pattern — combining URL state with server queries