or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

serialization.mddocs/

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

```