All built-in parsers are exported from the main nuqs package and implement the SingleParserBuilder interface, providing access to .withDefault() and .withOptions() methods.
String Parsers
parseAsString
Parses and serializes string values (identity parser).
export const parseAsString: SingleParserBuilder<string>
Implementation:
export const parseAsString: SingleParserBuilder<string> = createParser({
parse: v => v,
serialize: String
})
From: packages/nuqs/src/parsers.ts:230-233
Usage:
import { useQueryState, parseAsString } from 'nuqs'
const [search, setSearch] = useQueryState('q', parseAsString)
// URL: ?q=hello
// State: 'hello'
const [name, setName] = useQueryState('name', parseAsString.withDefault('Anonymous'))
// URL: (no query)
// State: 'Anonymous'
Behavior:
- Parse: Returns the input string as-is
- Serialize: Converts value to string using
String()
- Invalid input: Never returns
null (all strings are valid)
Number Parsers
parseAsInteger
Parses integers and serializes with rounding.
export const parseAsInteger: SingleParserBuilder<number>
Implementation:
export const parseAsInteger: SingleParserBuilder<number> = createParser({
parse: v => {
const int = parseInt(v)
return int == int ? int : null // NaN check at low bundle size cost
},
serialize: v => '' + Math.round(v)
})
From: packages/nuqs/src/parsers.ts:235-241
Usage:
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1))
// URL: ?page=5
// State: 5
setPage(3.14)
// URL: ?page=3 (rounded)
Behavior:
- Parse: Uses
parseInt(), returns null for NaN
- Serialize: Rounds to nearest integer with
Math.round()
- Invalid input:
'', 'abc', '3.14.15' → null
parseAsFloat
Parses floating-point numbers with full precision.
export const parseAsFloat: SingleParserBuilder<number>
Implementation:
export const parseAsFloat: SingleParserBuilder<number> = createParser({
parse: v => {
const float = parseFloat(v)
return float == float ? float : null // NaN check at low bundle size cost
},
serialize: String
})
From: packages/nuqs/src/parsers.ts:262-268
Usage:
const [price, setPrice] = useQueryState('price', parseAsFloat)
// URL: ?price=19.99
// State: 19.99
setPrice(0.1 + 0.2)
// URL: ?price=0.30000000000000004
// State: 0.30000000000000004
Behavior:
- Parse: Uses
parseFloat(), returns null for NaN
- Serialize: Uses
String() to preserve precision
- Invalid input:
'', 'abc' → null
parseAsHex
Parses hexadecimal numbers.
export const parseAsHex: SingleParserBuilder<number>
Implementation:
export const parseAsHex: SingleParserBuilder<number> = createParser({
parse: v => {
const int = parseInt(v, 16)
return int == int ? int : null // NaN check at low bundle size cost
},
serialize: v => {
const hex = Math.round(v).toString(16)
return (hex.length & 1 ? '0' : '') + hex
}
})
From: packages/nuqs/src/parsers.ts:251-260
Usage:
const [color, setColor] = useQueryState('color', parseAsHex)
// URL: ?color=ff00cc
// State: 16711884
setColor(0xff)
// URL: ?color=0ff (zero-padded to even length)
Behavior:
- Parse: Uses
parseInt(v, 16) for hexadecimal conversion
- Serialize: Converts to hex string, zero-pads to even length
- Invalid input:
'', 'xyz' → null
parseAsIndex
Parses 1-based indices and stores them as 0-based.
export const parseAsIndex: SingleParserBuilder<number>
Implementation:
export const parseAsIndex: SingleParserBuilder<number> = createParser({
parse: v => {
const int = parseInt(v)
return int == int ? int - 1 : null // NaN check at low bundle size cost
},
serialize: v => '' + Math.round(v + 1)
})
From: packages/nuqs/src/parsers.ts:243-249
Usage:
const [tabIndex, setTabIndex] = useQueryState('tab', parseAsIndex.withDefault(0))
// URL: ?tab=1
// State: 0 (0-based index)
setTabIndex(2)
// URL: ?tab=3 (1-based in URL)
Behavior:
- Parse: Subtracts 1 to convert from 1-based to 0-based
- Serialize: Adds 1 to convert from 0-based to 1-based, rounds
- Use case: User-facing page numbers, tab indices
Boolean Parser
parseAsBoolean
Parses boolean values (case-insensitive 'true' only).
export const parseAsBoolean: SingleParserBuilder<boolean>
Implementation:
export const parseAsBoolean: SingleParserBuilder<boolean> = createParser({
parse: v => v.toLowerCase() === 'true',
serialize: String
})
From: packages/nuqs/src/parsers.ts:270-273
Usage:
const [darkMode, setDarkMode] = useQueryState('dark', parseAsBoolean)
// URL: ?dark=true
// State: true
// URL: ?dark=false
// State: false
// URL: ?dark=1
// State: false (only 'true' is truthy)
Behavior:
- Parse: Returns
true only for 'true' (case-insensitive), otherwise false
- Serialize: Uses
String() to produce 'true' or 'false'
- Invalid input: All input is valid - non-
'true' values return false
- Note:
'yes', '1', 'on' all parse as false
Date & Time Parsers
All date parsers use a custom equality function:
function compareDates(a: Date, b: Date) {
return a.valueOf() === b.valueOf()
}
From: packages/nuqs/src/parsers.ts:275-277
parseAsTimestamp
Parses Unix timestamps (milliseconds since epoch).
export const parseAsTimestamp: SingleParserBuilder<Date>
Implementation:
export const parseAsTimestamp: SingleParserBuilder<Date> = createParser({
parse: v => {
const ms = parseInt(v)
return ms == ms ? new Date(ms) : null // NaN check at low bundle size cost
},
serialize: (v: Date) => '' + v.valueOf(),
eq: compareDates
})
From: packages/nuqs/src/parsers.ts:283-290
Usage:
const [created, setCreated] = useQueryState('created', parseAsTimestamp)
// URL: ?created=1704067200000
// State: Date(2024-01-01T00:00:00.000Z)
setCreated(new Date('2024-06-15'))
// URL: ?created=1718409600000
Behavior:
- Parse: Converts millisecond timestamp to
Date object
- Serialize: Converts
Date to milliseconds with valueOf()
- Invalid input:
'', 'abc' → null
parseAsIsoDateTime
Parses ISO-8601 datetime strings with timezone.
export const parseAsIsoDateTime: SingleParserBuilder<Date>
Implementation:
export const parseAsIsoDateTime: SingleParserBuilder<Date> = createParser({
parse: v => {
const date = new Date(v)
// NaN check at low bundle size cost
return date.valueOf() == date.valueOf() ? date : null
},
serialize: (v: Date) => v.toISOString(),
eq: compareDates
})
From: packages/nuqs/src/parsers.ts:296-304
Usage:
const [due, setDue] = useQueryState('due', parseAsIsoDateTime)
// URL: ?due=2024-01-01T12:30:00.000Z
// State: Date(2024-01-01T12:30:00.000Z)
setDue(new Date())
// URL: ?due=2024-03-03T10:15:30.123Z (full ISO string)
Behavior:
- Parse: Uses
new Date(v) constructor, returns null for invalid dates
- Serialize: Uses
toISOString() for full precision with timezone
- Invalid input:
'', 'not-a-date' → null
parseAsIsoDate
Parses ISO-8601 date strings (YYYY-MM-DD) without time.
export const parseAsIsoDate: SingleParserBuilder<Date>
Implementation:
export const parseAsIsoDate: SingleParserBuilder<Date> = createParser({
parse: v => {
const date = new Date(v.slice(0, 10))
// NaN check at low bundle size cost
return date.valueOf() == date.valueOf() ? date : null
},
serialize: (v: Date) => v.toISOString().slice(0, 10),
eq: compareDates
})
From: packages/nuqs/src/parsers.ts:314-322
Usage:
const [startDate, setStartDate] = useQueryState('start', parseAsIsoDate)
// URL: ?start=2024-01-01
// State: Date(2024-01-01T00:00:00.000Z) (at 00:00 UTC)
setStartDate(new Date('2024-06-15T14:30:00'))
// URL: ?start=2024-06-15 (time stripped)
Behavior:
- Parse: Takes first 10 characters (YYYY-MM-DD), creates Date at 00:00:00 UTC
- Serialize: Extracts date portion from ISO string
- Invalid input:
'', 'not-a-date' → null
Enum & Literal Parsers
parseAsStringEnum
Parses string-based TypeScript enums.
export function parseAsStringEnum<Enum extends string>(
validValues: Enum[]
): SingleParserBuilder<Enum>
Implementation:
export function parseAsStringEnum<Enum extends string>(
validValues: Enum[]
): SingleParserBuilder<Enum> {
// Delegate implementation to parseAsStringLiteral to avoid duplication.
return parseAsStringLiteral(validValues as readonly Enum[])
}
From: packages/nuqs/src/parsers.ts:351-356
Usage:
enum Direction {
up = 'UP',
down = 'DOWN',
left = 'LEFT',
right = 'RIGHT'
}
const [direction, setDirection] = useQueryState(
'direction',
parseAsStringEnum<Direction>(Object.values(Direction))
.withDefault(Direction.up)
)
// URL: ?direction=UP
// State: Direction.up
setDirection(Direction.left)
// URL: ?direction=LEFT
Behavior:
- Parse: Returns value if it matches one of
validValues, otherwise null
- Serialize: Uses
String() on the enum value
- Invalid input: Values not in
validValues → null
parseAsStringLiteral
Parses string literal unions.
export function parseAsStringLiteral<const Literal extends string>(
validValues: readonly Literal[]
): SingleParserBuilder<Literal>
Implementation:
export function parseAsStringLiteral<const Literal extends string>(
validValues: readonly Literal[]
): SingleParserBuilder<Literal> {
return createParser({
parse: (query: string) => {
const asConst = query as unknown as Literal
return validValues.includes(asConst) ? asConst : null
},
serialize: String
})
}
From: packages/nuqs/src/parsers.ts:377-387
Usage:
const colors = ['red', 'green', 'blue'] as const
const [color, setColor] = useQueryState(
'color',
parseAsStringLiteral(colors).withDefault('red')
)
// Type: 'red' | 'green' | 'blue'
// URL: ?color=green
// State: 'green'
// URL: ?color=yellow
// State: 'red' (invalid, uses default)
Behavior:
- Parse: Returns value if in
validValues, otherwise null
- Serialize: Uses
String() on the literal
- Type safety: Requires
as const assertion for proper type inference
parseAsNumberLiteral
Parses number literal unions.
export function parseAsNumberLiteral<const Literal extends number>(
validValues: readonly Literal[]
): SingleParserBuilder<Literal>
Implementation:
export function parseAsNumberLiteral<const Literal extends number>(
validValues: readonly Literal[]
): SingleParserBuilder<Literal> {
return createParser({
parse: (query: string) => {
const asConst = parseFloat(query) as unknown as Literal
if (validValues.includes(asConst)) {
return asConst
}
return null
},
serialize: String
})
}
From: packages/nuqs/src/parsers.ts:408-421
Usage:
const diceSides = [1, 2, 3, 4, 5, 6] as const
const [side, setSide] = useQueryState(
'side',
parseAsNumberLiteral(diceSides).withDefault(4)
)
// Type: 1 | 2 | 3 | 4 | 5 | 6
// URL: ?side=3
// State: 3
// URL: ?side=7
// State: 4 (invalid, uses default)
Behavior:
- Parse: Uses
parseFloat(), returns value if in validValues, otherwise null
- Serialize: Uses
String() on the number
- Type safety: Requires
as const assertion
Collection Parsers
parseAsArrayOf
Parses comma-separated arrays with URI encoding.
export function parseAsArrayOf<ItemType>(
itemParser: SingleParser<ItemType>,
separator = ','
): SingleParserBuilder<ItemType[]>
Implementation:
export function parseAsArrayOf<ItemType>(
itemParser: SingleParser<ItemType>,
separator = ','
): SingleParserBuilder<ItemType[]> {
const itemEq = itemParser.eq ?? ((a: ItemType, b: ItemType) => a === b)
const encodedSeparator = encodeURIComponent(separator)
return createParser({
parse: query => {
if (query === '') {
// Empty query should not go through the split/map/filter logic
return [] as ItemType[]
}
return query
.split(separator)
.map((item, index) =>
safeParse(
itemParser.parse,
item.replaceAll(encodedSeparator, separator),
`[${index}]`
)
)
.filter(value => value !== null && value !== undefined) as ItemType[]
},
serialize: values =>
values
.map<string>(value => {
const str = itemParser.serialize
? itemParser.serialize(value)
: String(value)
return str.replaceAll(separator, encodedSeparator)
})
.join(separator),
eq(a, b) {
if (a === b) {
return true // Referentially stable
}
if (a.length !== b.length) {
return false
}
return a.every((value, index) => itemEq(value, b[index]!))
}
})
}
From: packages/nuqs/src/parsers.ts:466-510
Usage:
const [tags, setTags] = useQueryState(
'tags',
parseAsArrayOf(parseAsString)
)
// URL: ?tags=react,next,typescript
// State: ['react', 'next', 'typescript']
setTags(['hello,world', 'foo'])
// URL: ?tags=hello%2Cworld,foo (separator encoded)
// Custom separator
const [ids, setIds] = useQueryState(
'ids',
parseAsArrayOf(parseAsInteger, '|')
)
// URL: ?ids=1|2|3
// State: [1, 2, 3]
Behavior:
- Parse: Splits by separator, parses each item, filters out
null values
- Serialize: Serializes each item, URI-encodes separator characters, joins
- Invalid items: Filtered out (array contains only valid items)
- Empty array: Serializes to empty string
''
parseAsNativeArrayOf
Parses native array query parameters (e.g., ?tag=a&tag=b&tag=c).
export function parseAsNativeArrayOf<ItemType>(
itemParser: SingleParser<ItemType>
): ReturnType<MultiParserBuilder<ItemType[]>['withDefault']>
Implementation:
export function parseAsNativeArrayOf<ItemType>(
itemParser: SingleParser<ItemType>
): ReturnType<MultiParserBuilder<ItemType[]>['withDefault']> {
const itemEq = itemParser.eq ?? ((a: ItemType, b: ItemType) => a === b)
return createMultiParser({
parse: query => {
const parsed = query
.map((item, index) => safeParse(itemParser.parse, item, `[${index}]`))
.filter(value => value !== null && value !== undefined) as ItemType[]
return parsed.length === 0 ? null : parsed
},
serialize: values => {
const safeValues = Array.isArray(values) ? values : [values]
return safeValues.flatMap(value => {
const serialized = itemParser.serialize?.(value) ?? String(value)
return typeof serialized === 'string' ? [serialized] : [...serialized]
})
},
eq(a, b) {
if (a === b) {
return true // Referentially stable
}
if (a.length !== b.length) {
return false
}
return a.every((value, index) => itemEq(value, b[index]!))
}
}).withDefault([])
}
From: packages/nuqs/src/parsers.ts:512-541
Usage:
const [categories, setCategories] = useQueryState(
'category',
parseAsNativeArrayOf(parseAsString)
)
// URL: ?category=electronics&category=books&category=toys
// State: ['electronics', 'books', 'toys']
setCategories(['a', 'b'])
// URL: ?category=a&category=b
// With integers
const [userIds, setUserIds] = useQueryState(
'userId',
parseAsNativeArrayOf(parseAsInteger)
)
// URL: ?userId=1&userId=2&userId=3
// State: [1, 2, 3]
Behavior:
- Parse: Parses array of query values, filters out
null, returns null if empty
- Serialize: Returns array of strings for repeated parameters
- Default: Always includes
.withDefault([]) for non-nullable arrays
- Invalid items: Filtered out
JSON Parser
parseAsJson
Parses JSON-encoded objects with optional runtime validation.
export function parseAsJson<T>(
validator: ((value: unknown) => T | null) | StandardSchemaV1<T>
): SingleParserBuilder<T>
Implementation:
export function parseAsJson<T>(
validator: ((value: unknown) => T | null) | StandardSchemaV1<T>
): SingleParserBuilder<T> {
return createParser({
parse: query => {
try {
const obj = JSON.parse(query)
if ('~standard' in validator) {
const result = validator['~standard'].validate(obj)
if (result instanceof Promise) {
throw new Error(
'[nuqs] Only synchronous Standard Schemas are supported in parseAsJson.'
)
}
return result.issues ? null : result.value
}
return validator(obj)
} catch {
return null
}
},
serialize: value => JSON.stringify(value),
eq(a, b) {
// Check for referential equality first
return a === b || JSON.stringify(a) === JSON.stringify(b)
}
})
}
From: packages/nuqs/src/parsers.ts:430-457
Usage:
import { parseAsJson } from 'nuqs'
import { z } from 'zod'
const pointSchema = z.object({
x: z.number(),
y: z.number()
})
// With Zod schema (Standard Schema support)
const [point, setPoint] = useQueryState(
'point',
parseAsJson(pointSchema)
)
// URL: ?point=%7B%22x%22%3A10%2C%22y%22%3A20%7D
// State: { x: 10, y: 20 }
// With custom validator function
type Point = { x: number; y: number }
const [point, setPoint] = useQueryState(
'point',
parseAsJson<Point>((value: unknown) => {
if (
typeof value === 'object' &&
value !== null &&
'x' in value &&
'y' in value
) {
return value as Point
}
return null
})
)
setPoint({ x: 100, y: 200 })
// URL updated with URL-encoded JSON
Behavior:
- Parse: Parses JSON, validates with schema/function, returns
null on error
- Serialize: Uses
JSON.stringify()
- Equality: Compares referential equality first, then serialized JSON
- Validation: Supports Standard Schema (Zod, Valibot, ArkType) or custom functions
- Invalid input: Malformed JSON or validation failure →
null
Always validate JSON data with a schema library. Unvalidated JSON can introduce type safety issues.
Next Steps