0
# Server-Side Cache
1
2
Server-side utilities for accessing and parsing query parameters in React Server Components, providing type-safe query parameter handling with request-scoped caching.
3
4
## Capabilities
5
6
### createSearchParamsCache
7
8
Creates a request-scoped cache for parsing and accessing query parameters in server components.
9
10
```typescript { .api }
11
/**
12
* Creates a search params cache for server-side query parameter parsing
13
* @param parsers - Object mapping keys to their respective parsers
14
* @returns Cache instance with parse, get, and all methods
15
*/
16
function createSearchParamsCache<Parsers extends Record<string, ParserBuilder<any>>>(
17
parsers: Parsers
18
): SearchParamsCache<Parsers>;
19
20
interface SearchParamsCache<Parsers extends Record<string, ParserBuilder<any>>> {
21
/** Parse the incoming searchParams page prop using the parsers provided */
22
parse(searchParams: SearchParams): ParsedSearchParams<Parsers>;
23
parse(searchParams: Promise<SearchParams>): Promise<ParsedSearchParams<Parsers>>;
24
25
/** Get a single parsed value by key */
26
get<Key extends keyof Parsers>(key: Key): ParsedSearchParams<Parsers>[Key];
27
28
/** Get all parsed values */
29
all(): ParsedSearchParams<Parsers>;
30
}
31
32
type SearchParams = Record<string, string | string[] | undefined>;
33
34
type ParsedSearchParams<Parsers extends Record<string, ParserBuilder<any>>> = {
35
readonly [K in keyof Parsers]: inferParserType<Parsers[K]>;
36
};
37
38
type inferParserType<Input> =
39
Input extends ParserBuilder<any>
40
? Input extends ParserBuilder<any> & { defaultValue: infer Type }
41
? Type
42
: ReturnType<Input['parse']> | null
43
: never;
44
```
45
46
**Usage Examples:**
47
48
```typescript
49
// app/search/page.tsx
50
import { createSearchParamsCache, parseAsString, parseAsInteger, parseAsBoolean } from "nuqs/server";
51
52
// Define the search params schema
53
const searchParamsCache = createSearchParamsCache({
54
q: parseAsString,
55
page: parseAsInteger.withDefault(1),
56
category: parseAsString,
57
featured: parseAsBoolean.withDefault(false)
58
});
59
60
export default async function SearchPage({
61
searchParams
62
}: {
63
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
64
}) {
65
// Parse all query parameters (Next.js 15+ with Promise)
66
const { q, page, category, featured } = await searchParamsCache.parse(searchParams);
67
68
// Types are fully inferred:
69
// q: string | null
70
// page: number (always defined due to default)
71
// category: string | null
72
// featured: boolean (always defined due to default)
73
74
return (
75
<div>
76
<h1>Search Results</h1>
77
{q && <p>Searching for: {q}</p>}
78
<p>Page: {page}</p>
79
{category && <p>Category: {category}</p>}
80
<p>Featured only: {featured}</p>
81
</div>
82
);
83
}
84
85
// For Next.js 14 and earlier (synchronous searchParams)
86
export default function SearchPage({
87
searchParams
88
}: {
89
searchParams: { [key: string]: string | string[] | undefined }
90
}) {
91
const { q, page, category, featured } = searchParamsCache.parse(searchParams);
92
// ... rest of component
93
}
94
```
95
96
### Individual Parameter Access
97
98
Access individual parsed parameters without parsing the entire object.
99
100
```typescript { .api }
101
/**
102
* Get a single parsed value by key
103
* @param key - The key to retrieve from the cache
104
* @throws Error if parse() hasn't been called first or key doesn't exist
105
*/
106
get<Key extends keyof Parsers>(key: Key): ParsedSearchParams<Parsers>[Key];
107
108
/**
109
* Get all parsed values from the cache
110
* @throws Error if parse() hasn't been called first
111
*/
112
all(): ParsedSearchParams<Parsers>;
113
```
114
115
**Usage Examples:**
116
117
```typescript
118
// In a server component after calling parse()
119
export default async function ProductPage({ searchParams }: { searchParams: Promise<any> }) {
120
// Parse all parameters first
121
await searchParamsCache.parse(searchParams);
122
123
// Access individual values in child components
124
return (
125
<div>
126
<SearchFilters />
127
<ProductList />
128
</div>
129
);
130
}
131
132
// Child server component can access individual values
133
function SearchFilters() {
134
const query = searchParamsCache.get('q');
135
const category = searchParamsCache.get('category');
136
137
return (
138
<div>
139
<p>Current search: {query}</p>
140
<p>Category: {category}</p>
141
</div>
142
);
143
}
144
145
// Or access all values
146
function DebugInfo() {
147
const allParams = searchParamsCache.all();
148
return <pre>{JSON.stringify(allParams, null, 2)}</pre>;
149
}
150
```
151
152
### generateMetadata Integration
153
154
Use the cache in generateMetadata functions for SEO and page metadata.
155
156
```typescript
157
import { Metadata } from "next";
158
159
export async function generateMetadata({
160
searchParams
161
}: {
162
searchParams: Promise<any>
163
}): Promise<Metadata> {
164
const { q, category } = await searchParamsCache.parse(searchParams);
165
166
const title = q
167
? `Search results for "${q}"${category ? ` in ${category}` : ''}`
168
: 'Search';
169
170
return {
171
title,
172
description: `Find products${category ? ` in ${category}` : ''}`
173
};
174
}
175
```
176
177
### Advanced Parser Configuration
178
179
The cache works with all parser types and configurations.
180
181
```typescript
182
import {
183
createSearchParamsCache,
184
parseAsStringEnum,
185
parseAsArrayOf,
186
parseAsJson
187
} from "nuqs/server";
188
189
// Complex parser configuration
190
const advancedCache = createSearchParamsCache({
191
// Enum with default
192
status: parseAsStringEnum(['draft', 'published', 'archived'] as const)
193
.withDefault('published'),
194
195
// Array of strings
196
tags: parseAsArrayOf(parseAsString),
197
198
// JSON object
199
filters: parseAsJson<{
200
minPrice: number;
201
maxPrice: number;
202
brand: string;
203
}>(),
204
205
// Date range
206
startDate: parseAsIsoDateTime,
207
endDate: parseAsIsoDateTime,
208
});
209
210
export default async function AdvancedSearchPage({
211
searchParams
212
}: {
213
searchParams: Promise<any>
214
}) {
215
const params = await advancedCache.parse(searchParams);
216
217
// All types are properly inferred:
218
// params.status: 'draft' | 'published' | 'archived'
219
// params.tags: string[] | null
220
// params.filters: { minPrice: number; maxPrice: number; brand: string } | null
221
// params.startDate: Date | null
222
// params.endDate: Date | null
223
}
224
```
225
226
### Request Scoping and Caching
227
228
The cache is automatically scoped to each request using React's `cache` function.
229
230
```typescript
231
// The same cache instance can be used across multiple server components
232
// in the same request without re-parsing
233
234
// layout.tsx
235
export default async function Layout({
236
searchParams
237
}: {
238
searchParams: Promise<any>
239
}) {
240
await searchParamsCache.parse(searchParams);
241
242
return (
243
<div>
244
<Header />
245
<Sidebar />
246
<main>{children}</main>
247
</div>
248
);
249
}
250
251
// components/Header.tsx
252
function Header() {
253
const query = searchParamsCache.get('q'); // Uses cached value
254
return <SearchInput defaultValue={query} />;
255
}
256
257
// components/Sidebar.tsx
258
function Sidebar() {
259
const category = searchParamsCache.get('category'); // Uses cached value
260
return <CategoryFilter selected={category} />;
261
}
262
```
263
264
### Error Handling
265
266
The cache includes built-in error handling for common scenarios.
267
268
```typescript
269
// Error cases:
270
try {
271
// Throws if parse() hasn't been called first
272
const value = searchParamsCache.get('q');
273
} catch (error) {
274
console.error('Must call parse() before accessing values');
275
}
276
277
try {
278
// Throws if key doesn't exist in parser schema
279
const value = searchParamsCache.get('nonexistent');
280
} catch (error) {
281
console.error('Key not found in parser schema');
282
}
283
284
// Invalid query values are gracefully parsed as null
285
const params = await searchParamsCache.parse({
286
page: 'invalid-number', // Will be parsed as null
287
enabled: 'maybe' // Will be parsed as null (not 'true'/'false')
288
});
289
```