
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
use-url-state-reacthook
Advanced tools
A powerful React hook for managing state synchronized with URL search parameters. Perfect for creating shareable URLs, maintaining filters across page refreshes, and building user-friendly web applications with deep linking support.
# Using npm
npm install use-url-state-reacthook
# Using yarn
yarn add use-url-state-reacthook
# Using pnpm
pnpm add use-url-state-reacthook
import { useUrlState } from "use-url-state-reacthook";
function SearchFilters() {
const [filters, filtersApi] = useUrlState({
search: "",
category: "all",
page: 1,
});
return (
<div>
<input
value={filters.search}
onChange={(e) => filtersApi.set("search", e.target.value)}
placeholder="Search..."
/>
<select
value={filters.category}
onChange={(e) => filtersApi.set("category", e.target.value)}
>
<option value="all">All Categories</option>
<option value="tech">Technology</option>
<option value="design">Design</option>
</select>
<div>Current page: {filters.page}</div>
<button onClick={() => filtersApi.set("page", filters.page + 1)}>
Next Page
</button>
</div>
);
}
The URL will automatically update to something like: ?search=react&category=tech&page=2
useUrlState(defaults?, options?)Returns a tuple [state, api] where:
state: The current state objectapi: Object with methods to manipulate the state| Parameter | Type | Description |
|---|---|---|
defaults | T | (() => T) | Default values for the state (optional) |
options | UrlStateOptions<T> | Configuration options (optional) |
| Option | Type | Default | Description |
|---|---|---|---|
codecs | Partial<{ [K in keyof T]: Codec<T[K]> }> | {} | Custom serialization for specific properties |
sanitize | (draft: Partial<T>) => Partial<T> | undefined | Validation/sanitization function |
onChange | (state: T, meta) => void | undefined | Callback fired on state changes |
history | 'replace' | 'push' | 'replace' | Browser history behavior |
debounceMs | number | undefined | Debounce delay for URL updates |
syncOnPopState | boolean | true | Sync state on browser navigation |
namespace | string | undefined | Prefix for URL parameters |
| Method | Signature | Description |
|---|---|---|
setState | (updater: T | (prev: T) => T) => void | Replace entire state |
get | (key: keyof T) => T[key] | Get value of specific property |
set | (key: keyof T, value: T[key]) => void | Set specific property |
patch | (partial: Partial<T>) => void | Merge partial changes |
remove | (...keys: (keyof T)[]) => void | Remove properties |
clear | () => void | Clear all state |
import { useUrlState } from "use-url-state-reacthook";
function App() {
const [state, api] = useUrlState({ name: "", age: 0 });
return (
<div>
<input
value={state.name}
onChange={(e) => api.set("name", e.target.value)}
/>
<input
type="number"
value={state.age}
onChange={(e) => api.set("age", parseInt(e.target.value) || 0)}
/>
<button onClick={() => api.clear()}>Clear All</button>
</div>
);
}
interface Filters {
tags: string[];
dateRange: { start: Date; end: Date };
settings: { theme: string; lang: string };
}
const [filters, api] = useUrlState<Filters>(
{
tags: [],
dateRange: { start: new Date(), end: new Date() },
settings: { theme: "light", lang: "en" },
},
{
codecs: {
tags: {
parse: (str) => str.split(",").filter(Boolean),
format: (tags) => tags.join(","),
},
dateRange: {
parse: (str) => {
const [start, end] = str.split("|").map((d) => new Date(d));
return { start, end };
},
format: (range) =>
`${range.start.toISOString()}|${range.end.toISOString()}`,
},
},
}
);
const [userPrefs, api] = useUrlState(
{
theme: "light",
fontSize: 16,
language: "en",
},
{
sanitize: (draft) => ({
theme: ["light", "dark"].includes(draft.theme) ? draft.theme : "light",
fontSize: Math.max(12, Math.min(24, draft.fontSize || 16)),
language: ["en", "fr", "es"].includes(draft.language)
? draft.language
: "en",
}),
debounceMs: 300, // Wait 300ms before updating URL
onChange: (newState, { source }) => {
console.log(`Preferences updated from ${source}:`, newState);
// Save to analytics, localStorage, etc.
},
}
);
function Dashboard() {
// User filters (prefixed with 'user_')
const [userFilters, userApi] = useUrlState(
{
role: "all",
department: "all",
},
{ namespace: "user" }
);
// Product filters (prefixed with 'product_')
const [productFilters, productApi] = useUrlState(
{
category: "all",
inStock: true,
},
{ namespace: "product" }
);
// URL: ?user_role=admin&user_department=engineering&product_category=electronics&product_inStock=true
}
interface AppState {
filters: {
search: string;
category: string[];
priceRange: [number, number];
};
view: "grid" | "list";
sort: { field: string; direction: "asc" | "desc" };
}
const [appState, api] = useUrlState<AppState>({
filters: {
search: "",
category: [],
priceRange: [0, 1000],
},
view: "grid",
sort: { field: "name", direction: "asc" },
});
// Update nested properties
api.patch({
filters: {
...appState.filters,
search: "new search term",
},
});
// Toggle sort direction
api.set("sort", {
...appState.sort,
direction: appState.sort.direction === "asc" ? "desc" : "asc",
});
// Replace current URL (default)
const [state, api] = useUrlState(defaults, { history: "replace" });
// Create new history entries (enables back/forward navigation between state changes)
const [state, api] = useUrlState(defaults, { history: "push" });
// Don't sync state when user uses back/forward buttons
const [state, api] = useUrlState(defaults, { syncOnPopState: false });
// Debounce URL updates for better performance with rapid changes
const [searchState, api] = useUrlState(
{ query: "" },
{
debounceMs: 300, // Wait 300ms before updating URL
}
);
// Perfect for search inputs that update frequently
<input
value={searchState.query}
onChange={(e) => api.set("query", e.target.value)}
/>;
The hook is fully typed and provides excellent TypeScript integration:
interface UserFilters {
name: string;
roles: ("admin" | "user" | "guest")[];
isActive: boolean;
metadata?: { lastLogin: Date };
}
// Full type safety
const [filters, api] = useUrlState<UserFilters>({
name: "",
roles: [],
isActive: true,
});
// TypeScript knows the exact shape
api.set("name", "john"); // ✅ Valid
api.set("roles", ["admin", "user"]); // ✅ Valid
api.set("invalidProp", "value"); // ❌ TypeScript error
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
Happy coding! 🚀 If you find this hook useful, please consider giving it a ⭐ on GitHub!
FAQs
A React hook for managing URL state
The npm package use-url-state-reacthook receives a total of 10 weekly downloads. As such, use-url-state-reacthook popularity was classified as not popular.
We found that use-url-state-reacthook demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.