or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdparsers.mdquery-states.mdserialization.mdserver-cache.md

parsers.mddocs/

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

```