0
# URL Serialization
1
2
Utilities for generating query strings from typed values using parser configuration, enabling programmatic URL generation with the same type safety as query state hooks.
3
4
## Capabilities
5
6
### createSerializer
7
8
Creates a serializer function that generates query strings from typed values using the same parsers used by query state hooks.
9
10
```typescript { .api }
11
/**
12
* Creates a serializer function for generating query strings
13
* @param parsers - Object mapping keys to their respective parsers with optional defaults
14
* @returns Serializer function with multiple overloads
15
*/
16
function createSerializer<Parsers extends Record<string, ParserWithOptionalDefault<any>>>(
17
parsers: Parsers
18
): SerializerFunction<Parsers>;
19
20
type ParserWithOptionalDefault<T> = ParserBuilder<T> & { defaultValue?: T };
21
22
interface SerializerFunction<Parsers extends Record<string, ParserWithOptionalDefault<any>>> {
23
/** Generate a query string for the given values */
24
(values: Values<Parsers>): string;
25
26
/** Append/amend the query string of the given base with the given values */
27
(base: Base, values: Values<Parsers> | null): string;
28
}
29
30
type Base = string | URLSearchParams | URL;
31
type Values<Parsers extends Record<string, ParserWithOptionalDefault<any>>> = Partial<{
32
[K in keyof Parsers]?: ExtractParserType<Parsers[K]>;
33
}>;
34
35
type ExtractParserType<Parser> = Parser extends ParserBuilder<any>
36
? ReturnType<Parser['parseServerSide']>
37
: never;
38
```
39
40
**Usage Examples:**
41
42
```typescript
43
import { createSerializer, parseAsString, parseAsInteger, parseAsBoolean } from "nuqs";
44
45
// Define the serializer with the same parsers used in components
46
const searchSerializer = createSerializer({
47
q: parseAsString,
48
page: parseAsInteger.withDefault(1),
49
category: parseAsString,
50
featured: parseAsBoolean.withDefault(false)
51
});
52
53
// Generate query strings
54
const queryString1 = searchSerializer({
55
q: 'react',
56
page: 2,
57
category: 'libraries'
58
});
59
// Result: "?q=react&page=2&category=libraries"
60
61
const queryString2 = searchSerializer({
62
q: 'nextjs',
63
featured: true
64
});
65
// Result: "?q=nextjs&featured=true"
66
// Note: page is omitted because it equals the default value
67
68
// Clear all parameters
69
const emptyQuery = searchSerializer({});
70
// Result: ""
71
```
72
73
### Base URL Modification
74
75
Append or modify existing URLs with new query parameters.
76
77
```typescript { .api }
78
/**
79
* Append/amend the query string of the given base with the given values
80
* Existing search param values will be kept, unless:
81
* - the value is null, in which case the search param will be deleted
82
* - another value is given for an existing key, in which case the search param will be updated
83
*/
84
(base: Base, values: Partial<ExtractParserTypes<Parsers>> | null): string;
85
```
86
87
**Usage Examples:**
88
89
```typescript
90
// Starting with a base URL
91
const baseUrl = "/search?existing=value&other=param";
92
93
// Add/modify specific parameters
94
const newUrl1 = searchSerializer(baseUrl, {
95
q: 'typescript',
96
page: 3
97
});
98
// Result: "/search?existing=value&other=param&q=typescript&page=3"
99
100
// Clear specific parameters with null
101
const newUrl2 = searchSerializer(baseUrl, {
102
q: null,
103
category: 'tools'
104
});
105
// Result: "/search?existing=value&other=param&category=tools"
106
107
// Clear all managed parameters
108
const clearedUrl = searchSerializer(baseUrl, null);
109
// Result: "/search?existing=value&other=param"
110
// Only removes keys defined in the parser schema
111
112
// Working with URLSearchParams
113
const params = new URLSearchParams("?existing=value");
114
const urlWithParams = searchSerializer(params, {
115
q: 'search-term',
116
page: 1
117
});
118
// Result: "?existing=value&q=search-term&page=1"
119
120
// Working with URL objects
121
const url = new URL("https://example.com/search?existing=value");
122
const fullUrl = searchSerializer(url, {
123
q: 'react'
124
});
125
// Result: "https://example.com/search?existing=value&q=react"
126
```
127
128
### Advanced Serialization
129
130
Works with all parser types including arrays, enums, and custom parsers.
131
132
```typescript
133
import {
134
createSerializer,
135
parseAsArrayOf,
136
parseAsStringEnum,
137
parseAsJson,
138
parseAsIsoDateTime
139
} from "nuqs";
140
141
// Complex serializer with various data types
142
const advancedSerializer = createSerializer({
143
tags: parseAsArrayOf(parseAsString),
144
status: parseAsStringEnum(['active', 'inactive'] as const).withDefault('active'),
145
config: parseAsJson<{ theme: string; notifications: boolean }>(),
146
createdAfter: parseAsIsoDateTime
147
});
148
149
// Serialize complex data
150
const complexQuery = advancedSerializer({
151
tags: ['react', 'nextjs', 'typescript'],
152
status: 'active',
153
config: { theme: 'dark', notifications: true },
154
createdAfter: new Date('2023-01-01')
155
});
156
// Result: "?tags=react,nextjs,typescript&config=%7B%22theme%22:%22dark%22,%22notifications%22:true%7D&createdAfter=2023-01-01T00:00:00.000Z"
157
// Note: status omitted because it equals default value, config is JSON-encoded and URI-encoded
158
```
159
160
### Link Generation
161
162
Common pattern for generating links with query parameters in Next.js applications.
163
164
```typescript
165
import Link from "next/link";
166
167
// Search results component
168
function SearchResults({ currentQuery, currentPage, totalPages }) {
169
// Generate pagination links
170
const nextPageUrl = searchSerializer('/search', {
171
q: currentQuery,
172
page: currentPage + 1
173
});
174
175
const prevPageUrl = searchSerializer('/search', {
176
q: currentQuery,
177
page: Math.max(1, currentPage - 1)
178
});
179
180
// Generate filter links
181
const categoryLinks = ['tools', 'libraries', 'frameworks'].map(category => ({
182
category,
183
url: searchSerializer('/search', {
184
q: currentQuery,
185
category,
186
page: 1 // Reset to first page when changing category
187
})
188
}));
189
190
return (
191
<div>
192
{/* Pagination */}
193
{currentPage > 1 && (
194
<Link href={prevPageUrl}>Previous</Link>
195
)}
196
{currentPage < totalPages && (
197
<Link href={nextPageUrl}>Next</Link>
198
)}
199
200
{/* Category filters */}
201
{categoryLinks.map(({ category, url }) => (
202
<Link key={category} href={url}>
203
{category}
204
</Link>
205
))}
206
</div>
207
);
208
}
209
```
210
211
### Default Value Handling
212
213
The serializer respects default values and `clearOnDefault` settings from parsers.
214
215
```typescript
216
const parserWithDefaults = createSerializer({
217
page: parseAsInteger.withDefault(1),
218
sort: parseAsString.withDefault('name'),
219
enabled: parseAsBoolean.withDefault(false).withOptions({ clearOnDefault: true })
220
});
221
222
// Default values are omitted from URL
223
const url1 = parserWithDefaults({
224
page: 1, // Omitted (equals default)
225
sort: 'name', // Omitted (equals default)
226
enabled: false // Omitted (equals default + clearOnDefault: true)
227
});
228
// Result: ""
229
230
// Non-default values are included
231
const url2 = parserWithDefaults({
232
page: 2,
233
sort: 'date',
234
enabled: true
235
});
236
// Result: "?page=2&sort=date&enabled=true"
237
```
238
239
### Router Integration
240
241
Use with Next.js router for programmatic navigation.
242
243
```typescript
244
import { useRouter } from "next/navigation";
245
246
function SearchForm() {
247
const router = useRouter();
248
249
const handleSearch = (formData: FormData) => {
250
const searchParams = {
251
q: formData.get('query') as string,
252
category: formData.get('category') as string,
253
page: 1 // Reset to first page
254
};
255
256
const url = searchSerializer('/search', searchParams);
257
router.push(url);
258
};
259
260
return (
261
<form action={handleSearch}>
262
<input name="query" placeholder="Search..." />
263
<select name="category">
264
<option value="">All Categories</option>
265
<option value="tools">Tools</option>
266
<option value="libraries">Libraries</option>
267
</select>
268
<button type="submit">Search</button>
269
</form>
270
);
271
}
272
```