Type-safe URL query parameter management with minimal, human-readable encoding.
- 🎯 Type-safe: Full TypeScript support with generic
Param<T>interface - 📦 Tiny URLs: Smart encoding - omit defaults, use short keys,
+for spaces - ⚛️ React hooks:
useUrlParam()anduseUrlParams()for seamless integration - 🔧 Framework-agnostic: Core utilities work anywhere, React hooks are optional
- 🌳 Tree-shakeable: ESM + CJS builds with TypeScript declarations
- 0️⃣ Zero dependencies: Except React (peer dependency, optional)
- 🔁 Multi-value params: Support for repeated keys like
?tag=a&tag=b - #️⃣ Hash params: Use hash fragment (
#key=value) instead of query string
npm install use-prms
# or
pnpm add use-prmsimport { useUrlParam, boolParam, stringParam, intParam } from 'use-prms'
function MyComponent() {
const [zoom, setZoom] = useUrlParam('z', boolParam)
const [device, setDevice] = useUrlParam('d', stringParam())
const [count, setCount] = useUrlParam('n', intParam(10))
// URL: ?z&d=gym&n=5
// zoom = true, device = "gym", count = 5
return (
<div>
<button onClick={() => setZoom(!zoom)}>Toggle Zoom</button>
<input value={device ?? ''} onChange={e => setDevice(e.target.value)} />
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
</div>
)
}const [enabled, setEnabled] = useUrlParam('e', boolParam)
// ?e → true
// (absent) → falseconst [name, setName] = useUrlParam('n', stringParam()) // optional
const [mode, setMode] = useUrlParam('m', defStringParam('auto')) // with default
// ?n=foo → "foo"
// (absent) → undefined / "auto"const [count, setCount] = useUrlParam('c', intParam(0))
const [ratio, setRatio] = useUrlParam('r', floatParam(1.0))
const [id, setId] = useUrlParam('id', optIntParam) // number | null
// ?c=5&r=1.5&id=123 → 5, 1.5, 123
// (absent) → 0, 1.0, nullconst [theme, setTheme] = useUrlParam(
't',
enumParam('light', ['light', 'dark', 'auto'] as const)
)
// ?t=dark → "dark"
// ?t=invalid → "light" (warns in console)const [tags, setTags] = useUrlParam('tags', stringsParam([], ','))
const [ids, setIds] = useUrlParam('ids', numberArrayParam([]))
// ?tags=foo,bar,baz → ["foo", "bar", "baz"]
// ?ids=1,2,3 → [1, 2, 3]import { useMultiUrlParam, multiStringParam, multiIntParam } from 'use-prms'
const [tags, setTags] = useMultiUrlParam('tag', multiStringParam())
// ?tag=foo&tag=bar&tag=baz → ["foo", "bar", "baz"]
const [ids, setIds] = useMultiUrlParam('id', multiIntParam())
// ?id=1&id=2&id=3 → [1, 2, 3]
// Also available: multiFloatParam()// Single value with short codes
const [metric, setMetric] = useUrlParam('y', codeParam('Rides', {
Rides: 'r',
Minutes: 'm',
}))
// ?y=m → "Minutes", omitted for default "Rides"
// Multi-value with short codes (omits when all selected)
const [regions, setRegions] = useUrlParam('r', codesParam(
['NYC', 'JC', 'HOB'],
{ NYC: 'n', JC: 'j', HOB: 'h' }
))
// ?r=nj → ["NYC", "JC"], omitted when all three selectedconst [page, setPage] = useUrlParam('p', paginationParam(20))
// Encodes offset + pageSize compactly using + as delimiter:
// { offset: 0, pageSize: 20 } → (omitted)
// { offset: 0, pageSize: 50 } → ?p=+50
// { offset: 100, pageSize: 20 } → ?p=100
// { offset: 100, pageSize: 50 } → ?p=100+50Create your own param encoders/decoders:
import type { Param } from 'use-prms'
// Example: Compact date encoding (YYMMDD)
const dateParam: Param<Date> = {
encode: (date) => {
const yy = String(date.getFullYear()).slice(-2)
const mm = String(date.getMonth() + 1).padStart(2, '0')
const dd = String(date.getDate()).padStart(2, '0')
return `${yy}${mm}${dd}`
},
decode: (str) => {
if (!str || str.length !== 6) return new Date()
const yy = parseInt('20' + str.slice(0, 2), 10)
const mm = parseInt(str.slice(2, 4), 10) - 1
const dd = parseInt(str.slice(4, 6), 10)
return new Date(yy, mm, dd)
}
}
const [date, setDate] = useUrlParam('d', dateParam)
// ?d=251123 → Date(2025, 10, 23)Use useUrlParams() to update multiple parameters atomically:
import { useUrlParams, intParam, boolParam } from 'use-prms'
const { values, setValues } = useUrlParams({
page: intParam(1),
size: intParam(20),
grid: boolParam
})
// Update multiple params at once (single history entry)
setValues({ page: 2, size: 50 })- Spaces: Encoded as
+(standard form-urlencoded) - Defaults: Omitted from URL (keeps URLs minimal)
- Booleans: Present = true (
?z), absent = false - Empty values: Valueless params (
?keywithout=)
Example:
const [devices, setDevices] = useUrlParam('d', stringsParam([], ' '))
setDevices(['gym', 'bedroom'])
// URL: ?d=gym+bedroomUse the core utilities without React:
import { boolParam, serializeParams, parseParams } from 'use-prms'
// Encode
const params = { z: boolParam.encode(true), d: 'gym' }
const search = serializeParams(params) // "z&d=gym"
// Decode
const parsed = parseParams(window.location.search)
const zoom = boolParam.decode(parsed.z) // trueUse hash fragment (#key=value) instead of query string (?key=value):
// Just change the import path
import { useUrlParam, boolParam } from 'use-prms/hash'
const [zoom, setZoom] = useUrlParam('z', boolParam)
// URL: https://example.com/#z (instead of ?z)Same API, different URL location. Useful when query strings conflict with server routing or you want params to survive page reloads without server involvement.
React hook for managing a single URL parameter.
key: Query parameter keyparam: Param encoder/decoderpush: Use pushState (true) or replaceState (false, default)- Returns:
[value: T, setValue: (value: T) => void]
React hook for managing multiple URL parameters together.
params: Object mapping keys to Param typespush: Use pushState (true) or replaceState (false, default)- Returns:
{ values, setValues }
React hook for managing a multi-value URL parameter (repeated keys).
key: Query parameter keyparam: MultiParam encoder/decoderpush: Use pushState (true) or replaceState (false, default)- Returns:
[value: T, setValue: (value: T) => void]
Bidirectional encoder/decoder interface:
type Param<T> = {
encode: (value: T) => string | undefined
decode: (encoded: string | undefined) => T
}Multi-value encoder/decoder interface:
type MultiParam<T> = {
encode: (value: T) => string[]
decode: (encoded: string[]) => T
}| Param | Type | Description |
|---|---|---|
boolParam |
Param<boolean> |
?key = true, absent = false |
stringParam(init?) |
Param<string | undefined> |
Optional string |
defStringParam(init) |
Param<string> |
Required string with default |
intParam(init) |
Param<number> |
Integer with default |
floatParam(init) |
Param<number> |
Float with default |
optIntParam |
Param<number | null> |
Optional integer |
enumParam(init, values) |
Param<T> |
Validated enum |
stringsParam(init?, delim?) |
Param<string[]> |
Delimiter-separated strings |
numberArrayParam(init?) |
Param<number[]> |
Comma-separated numbers |
codeParam(init, codeMap) |
Param<T> |
Enum with short URL codes |
codesParam(allValues, codeMap, sep?) |
Param<T[]> |
Multi-value with short codes |
paginationParam(defaultSize, validSizes?) |
Param<Pagination> |
Offset + page size |
| Param | Type | Description |
|---|---|---|
multiStringParam(init?) |
MultiParam<string[]> |
Repeated string params |
multiIntParam(init?) |
MultiParam<number[]> |
Repeated integer params |
multiFloatParam(init?) |
MultiParam<number[]> |
Repeated float params |
serializeParams(params): Convert params object to URL query string (deprecated, useserializeMultiParams)parseParams(source): Parse URL string or URLSearchParams to object (deprecated, useparseMultiParams)serializeMultiParams(params): Convert multi-value params to URL query stringparseMultiParams(source): Parse URL to multi-value params objectgetCurrentParams(): Get current URL params (browser only)updateUrl(params, push?): Update URL without reloading (browser only)
Projects using use-prms:
-
runsascoded/awair – Air quality dashboard with URL-persisted chart settings
Example:
awair.runsascoded.com/?d=+br&y=thZ&t=-3dd=+br: show default device + "bedroom" (+encodes space, leading space means "include default")y=thZ: left axis = temp (t), right axis = humidity (h), Y-axes don't start from zero (Z)t=-3d: time range = last 3 days
MIT