0
# Type-Safe Parsers
1
2
Comprehensive parser system for converting between URL query string values and typed JavaScript/TypeScript values, with built-in support for common data types and utilities for creating custom parsers.
3
4
## Capabilities
5
6
### Built-in Parsers
7
8
Ready-to-use parsers for common data types with fluent configuration API.
9
10
```typescript { .api }
11
/** String parser (default behavior) */
12
const parseAsString: ParserBuilder<string>;
13
14
/** Integer parser with NaN handling */
15
const parseAsInteger: ParserBuilder<number>;
16
17
/** Hexadecimal integer parser */
18
const parseAsHex: ParserBuilder<number>;
19
20
/** Float parser with NaN handling */
21
const parseAsFloat: ParserBuilder<number>;
22
23
/** Boolean parser (parses 'true'/'false' strings) */
24
const parseAsBoolean: ParserBuilder<boolean>;
25
26
/** Date parser from milliseconds since epoch */
27
const parseAsTimestamp: ParserBuilder<Date>;
28
29
/** Date parser from ISO-8601 strings */
30
const parseAsIsoDateTime: ParserBuilder<Date>;
31
```
32
33
**Usage Examples:**
34
35
```typescript
36
import {
37
parseAsString,
38
parseAsInteger,
39
parseAsBoolean,
40
parseAsTimestamp
41
} from "nuqs";
42
43
// Basic usage
44
const [name, setName] = useQueryState('name', parseAsString);
45
const [count, setCount] = useQueryState('count', parseAsInteger);
46
const [enabled, setEnabled] = useQueryState('enabled', parseAsBoolean);
47
48
// With default values
49
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
50
const [active, setActive] = useQueryState('active', parseAsBoolean.withDefault(false));
51
52
// Date handling
53
const [createdAt, setCreatedAt] = useQueryState('created', parseAsTimestamp);
54
const [updatedAt, setUpdatedAt] = useQueryState('updated', parseAsIsoDateTime);
55
```
56
57
### Enum and Literal Parsers
58
59
Type-safe parsers for restricted sets of values.
60
61
```typescript { .api }
62
/**
63
* String-based enums provide better type-safety for known sets of values
64
* @param validValues - The enum values you want to accept
65
*/
66
function parseAsStringEnum<Enum extends string>(validValues: Enum[]): ParserBuilder<Enum>;
67
68
/**
69
* String-based literals provide better type-safety for known sets of values
70
* @param validValues - The readonly list of allowed values
71
*/
72
function parseAsStringLiteral<Literal extends string>(
73
validValues: readonly Literal[]
74
): ParserBuilder<Literal>;
75
76
/**
77
* Number-based literals provide better type-safety for known sets of values
78
* @param validValues - The readonly list of allowed values
79
*/
80
function parseAsNumberLiteral<Literal extends number>(
81
validValues: readonly Literal[]
82
): ParserBuilder<Literal>;
83
```
84
85
**Usage Examples:**
86
87
```typescript
88
// Enum parser
89
enum Status {
90
DRAFT = 'draft',
91
PUBLISHED = 'published',
92
ARCHIVED = 'archived'
93
}
94
95
const [status, setStatus] = useQueryState(
96
'status',
97
parseAsStringEnum(Object.values(Status)).withDefault(Status.DRAFT)
98
);
99
100
// String literal parser
101
const themes = ['light', 'dark', 'auto'] as const;
102
const [theme, setTheme] = useQueryState(
103
'theme',
104
parseAsStringLiteral(themes).withDefault('auto')
105
);
106
107
// Number literal parser
108
const ratings = [1, 2, 3, 4, 5] as const;
109
const [rating, setRating] = useQueryState(
110
'rating',
111
parseAsNumberLiteral(ratings)
112
);
113
```
114
115
### Array Parser
116
117
Parser for comma-separated arrays with configurable item parsers.
118
119
```typescript { .api }
120
/**
121
* A comma-separated list of items
122
* @param itemParser - Parser for each individual item in the array
123
* @param separator - The character to use to separate items (default ',')
124
*/
125
function parseAsArrayOf<ItemType>(
126
itemParser: Parser<ItemType>,
127
separator?: string
128
): ParserBuilder<ItemType[]>;
129
```
130
131
**Usage Examples:**
132
133
```typescript
134
import { parseAsArrayOf, parseAsInteger, parseAsString } from "nuqs";
135
136
// Array of strings
137
const [tags, setTags] = useQueryState(
138
'tags',
139
parseAsArrayOf(parseAsString)
140
);
141
142
// Array of integers
143
const [ids, setIds] = useQueryState(
144
'ids',
145
parseAsArrayOf(parseAsInteger)
146
);
147
148
// Custom separator
149
const [categories, setCategories] = useQueryState(
150
'categories',
151
parseAsArrayOf(parseAsString, '|')
152
);
153
154
// Usage
155
setTags(['react', 'nextjs', 'typescript']);
156
setIds([1, 2, 3, 4]);
157
// URLs: ?tags=react,nextjs,typescript&ids=1,2,3,4
158
```
159
160
### JSON Parser
161
162
Parser for complex objects serialized as JSON in the query string.
163
164
```typescript { .api }
165
/**
166
* Encode any object shape into the querystring value as JSON
167
* Value is URI-encoded for safety, so it may not look nice in the URL
168
* @param parser - Optional parser (eg: Zod schema) to validate after JSON.parse
169
*/
170
function parseAsJson<T>(parser?: (value: unknown) => T): ParserBuilder<T>;
171
```
172
173
**Usage Examples:**
174
175
```typescript
176
import { parseAsJson } from "nuqs";
177
178
// Simple object
179
interface UserPrefs {
180
notifications: boolean;
181
theme: string;
182
language: string;
183
}
184
185
const [prefs, setPrefs] = useQueryState(
186
'prefs',
187
parseAsJson<UserPrefs>().withDefault({
188
notifications: true,
189
theme: 'light',
190
language: 'en'
191
})
192
);
193
194
// With validation using Zod
195
import { z } from 'zod';
196
197
const prefsSchema = z.object({
198
notifications: z.boolean(),
199
theme: z.enum(['light', 'dark']),
200
language: z.string().min(2)
201
});
202
203
const [validatedPrefs, setValidatedPrefs] = useQueryState(
204
'prefs',
205
parseAsJson(prefsSchema.parse)
206
);
207
```
208
209
### Custom Parser Creation
210
211
Factory function for creating custom parsers with full type safety.
212
213
```typescript { .api }
214
/**
215
* Wrap a set of parse/serialize functions into a builder pattern parser
216
* @param parser - Parser implementation with required parse and serialize functions
217
*/
218
function createParser<T>(
219
parser: Required<Pick<Parser<T>, 'parse' | 'serialize'>> & Partial<Pick<Parser<T>, 'eq'>>
220
): ParserBuilder<T>;
221
222
interface Parser<T> {
223
/**
224
* Convert a query string value into a state value.
225
* If the string value does not represent a valid state value,
226
* the parser should return null. Throwing an error is also supported.
227
*/
228
parse: (value: string) => T | null;
229
/** Render the state value into a query string value */
230
serialize?: (value: T) => string;
231
/**
232
* Check if two state values are equal.
233
* This is used when using the clearOnDefault value, to compare the default
234
* value with the set value. Useful for objects or arrays where referential
235
* equality check will not work.
236
*/
237
eq?: (a: T, b: T) => boolean;
238
}
239
```
240
241
**Usage Examples:**
242
243
```typescript
244
import { createParser } from "nuqs";
245
246
// Custom date parser for YYYY-MM-DD format
247
const parseAsDateString = createParser({
248
parse: (value: string) => {
249
const date = new Date(value);
250
return isNaN(date.getTime()) ? null : date;
251
},
252
serialize: (date: Date) => date.toISOString().split('T')[0]
253
});
254
255
// Custom object parser with specific serialization
256
interface Point {
257
x: number;
258
y: number;
259
}
260
261
const parseAsPoint = createParser({
262
parse: (value: string) => {
263
const [x, y] = value.split(',').map(Number);
264
return isNaN(x) || isNaN(y) ? null : { x, y };
265
},
266
serialize: (point: Point) => `${point.x},${point.y}`,
267
eq: (a: Point, b: Point) => a.x === b.x && a.y === b.y
268
});
269
270
const [point, setPoint] = useQueryState('point', parseAsPoint.withDefault({ x: 0, y: 0 }));
271
```
272
273
### Parser Builder API
274
275
All parsers implement the builder pattern for configuration and default values.
276
277
```typescript { .api }
278
interface ParserBuilder<T> extends Required<Parser<T>>, Options {
279
/**
280
* Specifying a default value makes the hook state non-nullable
281
* @param defaultValue - The default value to use when query is missing
282
*/
283
withDefault(
284
defaultValue: NonNullable<T>
285
): Omit<ParserBuilder<T>, 'parseServerSide'> & {
286
readonly defaultValue: NonNullable<T>;
287
parseServerSide(value: string | string[] | undefined): NonNullable<T>;
288
};
289
290
/**
291
* Set history type, shallow routing and scroll restoration options
292
* @param options - Configuration options for the parser
293
*/
294
withOptions<This, Shallow>(this: This, options: Options<Shallow>): This;
295
296
/**
297
* Use the parser in Server Components
298
* Note: when multiple queries are presented (eg: /?a=1&a=2), only the first will be parsed
299
* @param value - Query parameter value from page props
300
*/
301
parseServerSide(value: string | string[] | undefined): T | null;
302
}
303
304
interface Options<Shallow = unknown> {
305
/** How the query update affects page history (defaults to 'replace') */
306
history?: 'replace' | 'push';
307
/** Scroll to top after a query state update (defaults to false) */
308
scroll?: boolean;
309
/** Shallow mode keeps updates client-side only (defaults to true) */
310
shallow?: Extract<Shallow | boolean, boolean>;
311
/** Maximum time (ms) between URL query string updates (defaults to 50ms) */
312
throttleMs?: number;
313
/** React transition function for observing Server Component loading states */
314
startTransition?: TransitionStartFunction;
315
/** Clear key-value pair from URL when setting state to default value */
316
clearOnDefault?: boolean;
317
}
318
319
type TransitionStartFunction = (callback: () => void) => void;
320
```
321
322
**Configuration Examples:**
323
324
```typescript
325
// Parser with default value
326
const pageParser = parseAsInteger.withDefault(1);
327
328
// Parser with options
329
const searchParser = parseAsString.withOptions({
330
history: 'push',
331
throttleMs: 300
332
});
333
334
// Chaining configuration
335
const statusParser = parseAsStringEnum(['active', 'inactive'] as const)
336
.withDefault('active')
337
.withOptions({
338
clearOnDefault: true,
339
history: 'replace'
340
});
341
```
342
343
### Type Inference Helper
344
345
Utility type for extracting parser return types.
346
347
```typescript { .api }
348
/**
349
* Type helper to extract the underlying returned data type of a parser
350
* or of an object describing multiple parsers and their associated keys
351
*/
352
type inferParserType<Input> =
353
Input extends ParserBuilder<any>
354
? inferSingleParserType<Input>
355
: Input extends Record<string, ParserBuilder<any>>
356
? inferParserRecordType<Input>
357
: never;
358
```
359
360
**Usage Examples:**
361
362
```typescript
363
import { type inferParserType, parseAsInteger, parseAsBoolean } from "nuqs";
364
365
const intParser = parseAsInteger.withDefault(0);
366
const boolParser = parseAsBoolean;
367
368
type IntType = inferParserType<typeof intParser>; // number
369
type BoolType = inferParserType<typeof boolParser>; // boolean | null
370
371
const parsers = {
372
count: parseAsInteger.withDefault(0),
373
enabled: parseAsBoolean,
374
name: parseAsString
375
};
376
377
type ParsedValues = inferParserType<typeof parsers>;
378
// { count: number, enabled: boolean | null, name: string | null }
379
```