use-prms 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -30
- package/dist/hash.cjs +849 -52
- package/dist/hash.cjs.map +1 -1
- package/dist/hash.d.cts +1 -1
- package/dist/hash.d.ts +1 -1
- package/dist/hash.js +824 -50
- package/dist/hash.js.map +1 -1
- package/dist/index.cjs +849 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +451 -22
- package/dist/index.d.ts +451 -22
- package/dist/index.js +824 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,17 +14,19 @@ Type-safe URL-parameter (query and hash) management with minimal, human-readable
|
|
|
14
14
|
- [Custom Params](#custom)
|
|
15
15
|
- [Batch Updates](#batch)
|
|
16
16
|
- [URL Encoding](#encoding)
|
|
17
|
+
- [Binary Encoding](#binary)
|
|
17
18
|
- [Framework-Agnostic Core](#core)
|
|
18
19
|
- [Hash Params](#hash)
|
|
19
20
|
- [API Reference](#api)
|
|
20
21
|
- [Examples](#examples)
|
|
22
|
+
- [Reverse Inspo](#reverse-inspo)
|
|
21
23
|
- [License](#license)
|
|
22
24
|
|
|
23
25
|
## Features <a id="features"></a>
|
|
24
26
|
|
|
25
27
|
- 🎯 **Type-safe**: Full TypeScript support with generic `Param<T>` interface
|
|
26
28
|
- 📦 **Tiny URLs**: Smart encoding - omit defaults, use short keys, `+` for spaces
|
|
27
|
-
- ⚛️ **React hooks**: `
|
|
29
|
+
- ⚛️ **React hooks**: `useUrlState()` and `useUrlStates()` for seamless integration
|
|
28
30
|
- 🔧 **Framework-agnostic**: Core utilities work anywhere, React hooks are optional
|
|
29
31
|
- 🌳 **Tree-shakeable**: ESM + CJS builds with TypeScript declarations
|
|
30
32
|
- 0️⃣ **Zero dependencies**: Except React (peer dependency, optional)
|
|
@@ -44,12 +46,12 @@ pnpm add use-prms
|
|
|
44
46
|
## Quick Start <a id="quick-start"></a>
|
|
45
47
|
|
|
46
48
|
```typescript
|
|
47
|
-
import {
|
|
49
|
+
import { useUrlState, boolParam, stringParam, intParam } from 'use-prms'
|
|
48
50
|
|
|
49
51
|
function MyComponent() {
|
|
50
|
-
const [zoom, setZoom] =
|
|
51
|
-
const [device, setDevice] =
|
|
52
|
-
const [count, setCount] =
|
|
52
|
+
const [zoom, setZoom] = useUrlState('z', boolParam)
|
|
53
|
+
const [device, setDevice] = useUrlState('d', stringParam())
|
|
54
|
+
const [count, setCount] = useUrlState('n', intParam(10))
|
|
53
55
|
|
|
54
56
|
// URL: ?z&d=gym&n=5
|
|
55
57
|
// zoom = true, device = "gym", count = 5
|
|
@@ -68,31 +70,31 @@ function MyComponent() {
|
|
|
68
70
|
|
|
69
71
|
### Boolean
|
|
70
72
|
```typescript
|
|
71
|
-
const [enabled, setEnabled] =
|
|
73
|
+
const [enabled, setEnabled] = useUrlState('e', boolParam)
|
|
72
74
|
// ?e → true
|
|
73
75
|
// (absent) → false
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
### Strings
|
|
77
79
|
```typescript
|
|
78
|
-
const [name, setName] =
|
|
79
|
-
const [mode, setMode] =
|
|
80
|
+
const [name, setName] = useUrlState('n', stringParam()) // optional
|
|
81
|
+
const [mode, setMode] = useUrlState('m', defStringParam('auto')) // with default
|
|
80
82
|
// ?n=foo → "foo"
|
|
81
83
|
// (absent) → undefined / "auto"
|
|
82
84
|
```
|
|
83
85
|
|
|
84
86
|
### Numbers
|
|
85
87
|
```typescript
|
|
86
|
-
const [count, setCount] =
|
|
87
|
-
const [ratio, setRatio] =
|
|
88
|
-
const [id, setId] =
|
|
88
|
+
const [count, setCount] = useUrlState('c', intParam(0))
|
|
89
|
+
const [ratio, setRatio] = useUrlState('r', floatParam(1.0))
|
|
90
|
+
const [id, setId] = useUrlState('id', optIntParam) // number | null
|
|
89
91
|
// ?c=5&r=1.5&id=123 → 5, 1.5, 123
|
|
90
92
|
// (absent) → 0, 1.0, null
|
|
91
93
|
```
|
|
92
94
|
|
|
93
95
|
### Enums
|
|
94
96
|
```typescript
|
|
95
|
-
const [theme, setTheme] =
|
|
97
|
+
const [theme, setTheme] = useUrlState(
|
|
96
98
|
't',
|
|
97
99
|
enumParam('light', ['light', 'dark', 'auto'] as const)
|
|
98
100
|
)
|
|
@@ -102,20 +104,20 @@ const [theme, setTheme] = useUrlParam(
|
|
|
102
104
|
|
|
103
105
|
### Arrays (delimiter-separated)
|
|
104
106
|
```typescript
|
|
105
|
-
const [tags, setTags] =
|
|
106
|
-
const [ids, setIds] =
|
|
107
|
+
const [tags, setTags] = useUrlState('tags', stringsParam([], ','))
|
|
108
|
+
const [ids, setIds] = useUrlState('ids', numberArrayParam([]))
|
|
107
109
|
// ?tags=foo,bar,baz → ["foo", "bar", "baz"]
|
|
108
110
|
// ?ids=1,2,3 → [1, 2, 3]
|
|
109
111
|
```
|
|
110
112
|
|
|
111
113
|
### Multi-value Arrays (repeated keys)
|
|
112
114
|
```typescript
|
|
113
|
-
import {
|
|
115
|
+
import { useMultiUrlState, multiStringParam, multiIntParam } from 'use-prms'
|
|
114
116
|
|
|
115
|
-
const [tags, setTags] =
|
|
117
|
+
const [tags, setTags] = useMultiUrlState('tag', multiStringParam())
|
|
116
118
|
// ?tag=foo&tag=bar&tag=baz → ["foo", "bar", "baz"]
|
|
117
119
|
|
|
118
|
-
const [ids, setIds] =
|
|
120
|
+
const [ids, setIds] = useMultiUrlState('id', multiIntParam())
|
|
119
121
|
// ?id=1&id=2&id=3 → [1, 2, 3]
|
|
120
122
|
|
|
121
123
|
// Also available: multiFloatParam()
|
|
@@ -124,14 +126,14 @@ const [ids, setIds] = useMultiUrlParam('id', multiIntParam())
|
|
|
124
126
|
### Compact Code Mapping
|
|
125
127
|
```typescript
|
|
126
128
|
// Single value with short codes
|
|
127
|
-
const [metric, setMetric] =
|
|
129
|
+
const [metric, setMetric] = useUrlState('y', codeParam('Rides', {
|
|
128
130
|
Rides: 'r',
|
|
129
131
|
Minutes: 'm',
|
|
130
132
|
}))
|
|
131
133
|
// ?y=m → "Minutes", omitted for default "Rides"
|
|
132
134
|
|
|
133
135
|
// Multi-value with short codes (omits when all selected)
|
|
134
|
-
const [regions, setRegions] =
|
|
136
|
+
const [regions, setRegions] = useUrlState('r', codesParam(
|
|
135
137
|
['NYC', 'JC', 'HOB'],
|
|
136
138
|
{ NYC: 'n', JC: 'j', HOB: 'h' }
|
|
137
139
|
))
|
|
@@ -140,7 +142,7 @@ const [regions, setRegions] = useUrlParam('r', codesParam(
|
|
|
140
142
|
|
|
141
143
|
### Pagination
|
|
142
144
|
```typescript
|
|
143
|
-
const [page, setPage] =
|
|
145
|
+
const [page, setPage] = useUrlState('p', paginationParam(20))
|
|
144
146
|
// Encodes offset + pageSize compactly using + as delimiter:
|
|
145
147
|
// { offset: 0, pageSize: 20 } → (omitted)
|
|
146
148
|
// { offset: 0, pageSize: 50 } → ?p=+50
|
|
@@ -172,18 +174,18 @@ const dateParam: Param<Date> = {
|
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
const [date, setDate] =
|
|
177
|
+
const [date, setDate] = useUrlState('d', dateParam)
|
|
176
178
|
// ?d=251123 → Date(2025, 10, 23)
|
|
177
179
|
```
|
|
178
180
|
|
|
179
181
|
## Batch Updates <a id="batch"></a>
|
|
180
182
|
|
|
181
|
-
Use `
|
|
183
|
+
Use `useUrlStates()` to update multiple parameters atomically:
|
|
182
184
|
|
|
183
185
|
```typescript
|
|
184
|
-
import {
|
|
186
|
+
import { useUrlStates, intParam, boolParam } from 'use-prms'
|
|
185
187
|
|
|
186
|
-
const { values, setValues } =
|
|
188
|
+
const { values, setValues } = useUrlStates({
|
|
187
189
|
page: intParam(1),
|
|
188
190
|
size: intParam(20),
|
|
189
191
|
grid: boolParam
|
|
@@ -202,11 +204,72 @@ setValues({ page: 2, size: 50 })
|
|
|
202
204
|
|
|
203
205
|
Example:
|
|
204
206
|
```typescript
|
|
205
|
-
const [devices, setDevices] =
|
|
207
|
+
const [devices, setDevices] = useUrlState('d', stringsParam([], ' '))
|
|
206
208
|
setDevices(['gym', 'bedroom'])
|
|
207
209
|
// URL: ?d=gym+bedroom
|
|
208
210
|
```
|
|
209
211
|
|
|
212
|
+
## Binary Encoding <a id="binary"></a>
|
|
213
|
+
|
|
214
|
+
For complex data that doesn't fit well into string encoding, `use-prms` provides binary encoding utilities with URL-safe base64.
|
|
215
|
+
|
|
216
|
+
### BitBuffer
|
|
217
|
+
|
|
218
|
+
Low-level bit packing for custom binary formats:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { BitBuffer } from 'use-prms'
|
|
222
|
+
|
|
223
|
+
// Encoding
|
|
224
|
+
const buf = new BitBuffer()
|
|
225
|
+
buf.encodeInt(myEnum, 3) // 3 bits for enum (0-7)
|
|
226
|
+
buf.encodeInt(myCount, 8) // 8 bits for count (0-255)
|
|
227
|
+
buf.encodeBigInt(myId, 48) // 48 bits for ID
|
|
228
|
+
const urlParam = buf.toBase64()
|
|
229
|
+
|
|
230
|
+
// Decoding
|
|
231
|
+
const buf = BitBuffer.fromBase64(urlParam)
|
|
232
|
+
const myEnum = buf.decodeInt(3)
|
|
233
|
+
const myCount = buf.decodeInt(8)
|
|
234
|
+
const myId = buf.decodeBigInt(48)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Float Params
|
|
238
|
+
|
|
239
|
+
Encode floats compactly as base64:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { floatParam } from 'use-prms'
|
|
243
|
+
|
|
244
|
+
// Lossless (11 chars, exact IEEE 754)
|
|
245
|
+
const [zoom, setZoom] = useUrlState('z', floatParam(1.0))
|
|
246
|
+
|
|
247
|
+
// Lossy (fewer chars, configurable precision)
|
|
248
|
+
const [lat, setLat] = useUrlState('lat', floatParam({
|
|
249
|
+
default: 0,
|
|
250
|
+
exp: 5, // exponent bits
|
|
251
|
+
mant: 22, // mantissa bits (~7 decimal digits)
|
|
252
|
+
}))
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Custom Alphabets
|
|
256
|
+
|
|
257
|
+
Choose between standard base64url or ASCII-sorted alphabet:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { ALPHABETS, binaryParam, floatParam } from 'use-prms'
|
|
261
|
+
|
|
262
|
+
// Standard RFC 4648 (default)
|
|
263
|
+
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
|
|
264
|
+
|
|
265
|
+
// ASCII-sorted (lexicographic sort = numeric sort)
|
|
266
|
+
// -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
|
|
267
|
+
|
|
268
|
+
const param = floatParam({ default: 0, alphabet: 'sortable' })
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The `sortable` alphabet is useful when encoded strings need to sort in the same order as their numeric values (e.g., for database indexing).
|
|
272
|
+
|
|
210
273
|
## Framework-Agnostic Core <a id="core"></a>
|
|
211
274
|
|
|
212
275
|
Use the core utilities without React:
|
|
@@ -229,9 +292,9 @@ Use hash fragment (`#key=value`) instead of query string (`?key=value`):
|
|
|
229
292
|
|
|
230
293
|
```typescript
|
|
231
294
|
// Just change the import path
|
|
232
|
-
import {
|
|
295
|
+
import { useUrlState, boolParam } from 'use-prms/hash'
|
|
233
296
|
|
|
234
|
-
const [zoom, setZoom] =
|
|
297
|
+
const [zoom, setZoom] = useUrlState('z', boolParam)
|
|
235
298
|
// URL: https://example.com/#z (instead of ?z)
|
|
236
299
|
```
|
|
237
300
|
|
|
@@ -239,7 +302,7 @@ Same API, different URL location. Useful when query strings conflict with server
|
|
|
239
302
|
|
|
240
303
|
## API Reference <a id="api"></a>
|
|
241
304
|
|
|
242
|
-
### `
|
|
305
|
+
### `useUrlState<T>(key: string, param: Param<T>, push?: boolean)`
|
|
243
306
|
|
|
244
307
|
React hook for managing a single URL parameter.
|
|
245
308
|
|
|
@@ -248,7 +311,7 @@ React hook for managing a single URL parameter.
|
|
|
248
311
|
- `push`: Use pushState (true) or replaceState (false, default)
|
|
249
312
|
- Returns: `[value: T, setValue: (value: T) => void]`
|
|
250
313
|
|
|
251
|
-
### `
|
|
314
|
+
### `useUrlStates<P>(params: P, push?: boolean)`
|
|
252
315
|
|
|
253
316
|
React hook for managing multiple URL parameters together.
|
|
254
317
|
|
|
@@ -256,7 +319,7 @@ React hook for managing multiple URL parameters together.
|
|
|
256
319
|
- `push`: Use pushState (true) or replaceState (false, default)
|
|
257
320
|
- Returns: `{ values, setValues }`
|
|
258
321
|
|
|
259
|
-
### `
|
|
322
|
+
### `useMultiUrlState<T>(key: string, param: MultiParam<T>, push?: boolean)`
|
|
260
323
|
|
|
261
324
|
React hook for managing a multi-value URL parameter (repeated keys).
|
|
262
325
|
|
|
@@ -312,6 +375,17 @@ type MultiParam<T> = {
|
|
|
312
375
|
| `multiIntParam(init?)` | `MultiParam<number[]>` | Repeated integer params |
|
|
313
376
|
| `multiFloatParam(init?)` | `MultiParam<number[]>` | Repeated float params |
|
|
314
377
|
|
|
378
|
+
### Binary Encoding
|
|
379
|
+
|
|
380
|
+
| Export | Description |
|
|
381
|
+
|--------|-------------|
|
|
382
|
+
| `BitBuffer` | Bit-level buffer for packing/unpacking arbitrary bit widths |
|
|
383
|
+
| `binaryParam(opts)` | Create param from `toBytes`/`fromBytes` converters |
|
|
384
|
+
| `base64Param(toBytes, fromBytes)` | Shorthand for `binaryParam` |
|
|
385
|
+
| `base64Encode(bytes, opts?)` | Encode `Uint8Array` to base64 string |
|
|
386
|
+
| `base64Decode(str, opts?)` | Decode base64 string to `Uint8Array` |
|
|
387
|
+
| `ALPHABETS` | Preset alphabets: `rfc4648` (default), `sortable` (ASCII-ordered) |
|
|
388
|
+
|
|
315
389
|
### Core Utilities
|
|
316
390
|
|
|
317
391
|
- `serializeParams(params)`: Convert params object to URL query string *(deprecated, use `serializeMultiParams`)*
|
|
@@ -349,6 +423,107 @@ Projects using `use-prms`:
|
|
|
349
423
|
[use-kbd-gh]: https://github.com/runsascoded/use-kbd
|
|
350
424
|
[use-kbd-search]: https://github.com/search?q=repo%3Arunsascoded%2Fuse-kbd+use-prms&type=code
|
|
351
425
|
|
|
426
|
+
## Reverse Inspo <a id="reverse-inspo"></a>
|
|
427
|
+
|
|
428
|
+
It's nice when URLs are concise but also reasonably human-readable. Some examples I've seen in the wild that exhibit room for improvement:
|
|
429
|
+
|
|
430
|
+
### UUID Soup (OpenAI Careers)
|
|
431
|
+
```
|
|
432
|
+
https://openai.com/careers/search/
|
|
433
|
+
?l=e8062547-b090-4206-8f1e-7329e0014e98%2C07ed9191-5bc6-421b-9883-f1ac2e276ad7
|
|
434
|
+
&c=e1e973fe-6f0a-475f-9361-a9b6c095d869%2Cf002fe09-4cec-46b0-8add-8bf9ff438a62
|
|
435
|
+
%2Cab2b9da4-24a4-47df-8bed-1ed5a39c7036%2C687d87ec-1505-40e7-a2b5-cc7f31c0ea48
|
|
436
|
+
%2Cd36236ec-fb74-49bd-bd3f-9d8365e2e2cb%2C27c9a852-c401-450e-9480-d3b507b8f64a
|
|
437
|
+
%2C6dd4a467-446d-4093-8d57-d4633a571123%2C7cba3ac0-2b6e-4d52-ad38-e39a5f61c73f
|
|
438
|
+
%2C0f06f916-a404-414f-813f-6ac7ff781c61%2Cfb2b77c5-5f20-4a93-a1c4-c3d640d88e04
|
|
439
|
+
```
|
|
440
|
+
12 UUIDs for location and category filters. Each UUID is 36 characters. With short codes, this could be `?l=sf,ny&c=eng,res,des,acct,data,hr,infra,accel,acq,bus`.
|
|
441
|
+
|
|
442
|
+
### Encrypted Blobs (Supercast, Priceline)
|
|
443
|
+
```
|
|
444
|
+
https://feeds.supercast.com/episodes/8a1aa9e2dde4319825e6a8171b4d51fa1835ef4a
|
|
445
|
+
6730170db60a92c8f0670bb08c3cef884f0e4288c970c980083820e89cd692f582c44cde
|
|
446
|
+
544c7aae86fc721f69ed9f695a43e5e21f4d344b32e70bae48a8fe0ae8b472d99502041a
|
|
447
|
+
bad3dc650a6973653c094eae0631f637d96bb42ab5d26b8ea6b1638b7ffa23f66e46282b
|
|
448
|
+
52970b59b2c13f9e6214251ad793be244bb9dc7e5bd7cefe77b6ec71b06c85e3bc9c194a
|
|
449
|
+
d4ca10b27cfd7b8b1c181b3d9aea144bb978d1d790f08d89049d5a29a477651f1b799eec
|
|
450
|
+
827ed95209dc741207e2b331170cb01c625d51982913eb8757ef2b2037235624a7bbfab9
|
|
451
|
+
8a641e98a507ee096d0678c8ab458fd87731a9a7a0bdc87a99fbbfe684be10f5d4259265
|
|
452
|
+
68b041a308017ce2901b3c6bf4b3bc89a2b13f3c54047d2fc5f69e9a5053b5e5bb2e0f70
|
|
453
|
+
a2a77d9a25c97b890faec970e29f1c6961b1e00ccd1d8ba9c4006ba8b657193fe5a5b8e4
|
|
454
|
+
6aa6a86492c381c79afe09d347d25c550c195d080695e3b97c012be3ebf1e2e64bd9f6c2
|
|
455
|
+
9977e4b34184858bcf99164010dc3746f49d90df559f7dfa6f029f50f35f7777c44d1247
|
|
456
|
+
ecdfc7861969f172d63eb3acc620ac25919cdc5caf4397793b7d564ccc4b0519118027.mp3
|
|
457
|
+
?key=8kSKDMBUEi2TCGyzhdzZBVSN&v=0
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
https://www.priceline.com/relax/at/2003205/from/20240628/to/20240629/rooms/1
|
|
462
|
+
?meta-id=AyOy_-ov9Edvq6cYGWUbaO9KdvlksSZCnHtEiIUqbvfIqUNLp0ZV0WiDB-MXSyZhxM
|
|
463
|
+
mSw6xJm0HTePNNo_NwvV_Mzo1DeUvJhE53dMnjIqnwb7rfeGGSHOuOML_0zcWCYppfcv6Cf8T
|
|
464
|
+
Na_TIadYlC8PJkvC_qY7bm0lXIqygsn03MyXPyXyUCXNRcKiIm2QS5bWoOeiO48zWgHRtLUDm
|
|
465
|
+
cNx8o6rdlIukl18vqu8RQYajSd3Yt9bbWwDTBjeEduJ2sfoh4Mi3XtGzbqy8YpUrRgIUCGCYf
|
|
466
|
+
DHBdaS47dUkqKfqtQvY7yCPh9Y4YNUZtt9w-TRqndd6AdvbOMprSAbawg8IU5wIj-yEbZr82e
|
|
467
|
+
CcQg2dylETYccSaRK07WHSEJx7
|
|
468
|
+
&pclnId=0571D9ABC99167E702D55CD454625E1BD51BC6742D4EB3A6869799404CB9B21E0E31
|
|
469
|
+
CA463BDC3DE5A56EDB9C6B55C3F06EB5CBBC77502608C5279D0943A5F2545B3F0E4366F3FB
|
|
470
|
+
CCDE32424FB9D2CC10B7E2B68DD59C89151023C9B800744FDDF1C7D85AEB2CF27E
|
|
471
|
+
&gid=5369&cityId=3000035889&cur=USD&backlink-id=gotjhpxt5bp
|
|
472
|
+
```
|
|
473
|
+
900 hex characters, 400-char tracking IDs, session blobs.
|
|
474
|
+
|
|
475
|
+
### Tracking Parameter Avalanche (Apple TV)
|
|
476
|
+
```
|
|
477
|
+
https://tv.apple.com/us/show/severance/umc.cmc.1srk2goyh2q2zdxcx605w8vtx
|
|
478
|
+
?ign-itscg=MC_20000&ign-itsct=atvp_brand_omd
|
|
479
|
+
&mttn3pid=Google%20AdWords&mttnagencyid=a5e&mttncc=US
|
|
480
|
+
&mttnsiteid=143238&mttnsubad=OUS2019927_1-592764821446-m
|
|
481
|
+
&mttnsubkw=133111427260__zxnj5jSX_&mttnsubplmnt=
|
|
482
|
+
```
|
|
483
|
+
Seven `mttn*` tracking parameters that are excessively verbose (and come from a single ad click).
|
|
484
|
+
|
|
485
|
+
### Base64-Encoded Redirect URLs (Wired)
|
|
486
|
+
```
|
|
487
|
+
https://link.wired.com/external/39532383.1121/aHR0cHM6Ly9jb25kZW5hc3Quem9vbS
|
|
488
|
+
51cy93ZWJpbmFyL3JlZ2lzdGVyL1dOX29kcldRdE5uUkdhSUN3MHZob0N3ckE_dXRtX3Nvd
|
|
489
|
+
XJjZT1ubCZ1dG1fYnJhbmQ9d2lyZWQmdXRtX21haWxpbmc9V0lSX1BheXdhbGxTdWJzXzA0
|
|
490
|
+
MjMyNV9TcGVjaWFsX0FJVW5sb2NrZWRfTkxTVUJTSW52aXRlJnV0bV9jYW1wYWlnbj1hdWQ
|
|
491
|
+
tZGV2JnV0bV9tZWRpdW09ZW1haWwmdXRtX2NvbnRlbnQ9V0lSX1BheXdhbGxTdWJzXzA0Mj
|
|
492
|
+
MyNV9TcGVjaWFsX0FJVW5sb2NrZWRfTkxTVUJTSW52aXRlJmJ4aWQ9NWNjOWUwZjdmYzk0M
|
|
493
|
+
mQxM2ViMWY0YjhjJmNuZGlkPTUwNTQyMzY4Jmhhc2hhPTQwODY5ZjRmY2ExOWRkZjU2NTUz
|
|
494
|
+
M2Q2NzMxYmVkMTExJmhhc2hiPWFjNzQxNjk4NjkyMTE1YWExOGRkNzg5N2JjMTIxNmIwNWM
|
|
495
|
+
0YmI2ODgmaGFzaGM9ZTA5YTA4NzM0MTM3NDA4ODE3NzZlNjExNzQ3NzQ3NDM5ZDYzMGM2YT
|
|
496
|
+
k0NGVmYTIwOGFhMzhhYTMwZjljYTE0NyZlc3JjPU9JRENfU0VMRUNUX0FDQ09VTlRfUEFHR
|
|
497
|
+
Q/5cc9e0f7fc942d13eb1f4b8cB8513f7ce
|
|
498
|
+
```
|
|
499
|
+
A URL containing another (base64-encoded) URL containing UTM params, hashes, and tracking IDs.
|
|
500
|
+
|
|
501
|
+
### Kitchen Sink (Grubhub)
|
|
502
|
+
```
|
|
503
|
+
https://www.grubhub.com/restaurant/bobs-noodle-house-123-main-st-newark/4857291
|
|
504
|
+
/grouporder/Xk7rPwchQfDsT3J9yCtghR
|
|
505
|
+
?pageNum=1&pageSize=20
|
|
506
|
+
&facet=scheduled%3Afalse&facet=orderType%3AALL
|
|
507
|
+
&includePartnerOrders=true&sorts=default&blockModal=true
|
|
508
|
+
&utm_source=grubhub_web&utm_medium=content_owned
|
|
509
|
+
&utm_campaign=product_sharedcart_join&utm_content=share-link
|
|
510
|
+
```
|
|
511
|
+
Session IDs, pagination defaults that could be omitted, boolean flags, four UTM parameters, and all more verbose than necessary, resulting in an unwieldy URL.
|
|
512
|
+
|
|
513
|
+
### The `use-prms` way
|
|
514
|
+
|
|
515
|
+
This may not be best in all cases, but `use-prms` encourages encoding the same information more compactly:
|
|
516
|
+
|
|
517
|
+
| Verbose | Compact | Meaning |
|
|
518
|
+
|----------------------------------------|----------------------------------|--------------------------------------|
|
|
519
|
+
| `?show_grid=true` | `?g` | Boolean flag |
|
|
520
|
+
| `?page_number=5&page_size=50` | `?p=5x50` | Compact, combined state |
|
|
521
|
+
| `?page_number=5&page_size=20` | `?p=5` | Default values omitted |
|
|
522
|
+
| `?category=e1e973fe-6f0a-...` | `?c=eng` | Short, human-readable codes for enums |
|
|
523
|
+
| `?latitude=40.7128&longitude=-74.0060` | `?ll=40.7128-74.0060` | Compact, combined state |
|
|
524
|
+
|
|
525
|
+
URLs are part of your UI. Treat them with the same care as your design.
|
|
526
|
+
|
|
352
527
|
## License <a id="license"></a>
|
|
353
528
|
|
|
354
529
|
MIT
|