0
# Pages Router (Legacy)
1
2
Complete reference for Next.js Pages Router with data fetching methods, API routes, and navigation.
3
4
> **Note**: The Pages Router is the legacy routing system. New projects should use the App Router for better performance and features.
5
6
The Pages Router provides traditional file-system based routing with data fetching methods including getStaticProps, getServerSideProps, and API routes for full-stack development.
7
8
## Capabilities
9
10
### useRouter Hook
11
12
Pages Router navigation hook for programmatic routing and accessing route state. Must be used in Client Components.
13
14
```typescript { .api }
15
import { useRouter } from 'next/router';
16
17
/**
18
* Returns the Pages Router instance
19
* @returns NextRouter with navigation methods and route state
20
*/
21
function useRouter(): NextRouter;
22
23
interface NextRouter {
24
/**
25
* The current pathname including dynamic route parameters
26
*/
27
pathname: string;
28
29
/**
30
* The query string parsed as an object, including dynamic route parameters
31
*/
32
query: ParsedUrlQuery;
33
34
/**
35
* The actual path shown in the browser
36
*/
37
asPath: string;
38
39
/**
40
* The base path configured in next.config.js
41
*/
42
basePath: string;
43
44
/**
45
* The active locale
46
*/
47
locale?: string;
48
49
/**
50
* All configured locales
51
*/
52
locales?: string[];
53
54
/**
55
* The default locale
56
*/
57
defaultLocale?: string;
58
59
/**
60
* Whether the router is ready and router fields are updated client-side
61
*/
62
isReady: boolean;
63
64
/**
65
* Whether the application is in preview mode
66
*/
67
isPreview: boolean;
68
69
/**
70
* Whether the current page is a fallback page
71
*/
72
isFallback: boolean;
73
74
/**
75
* Navigate to a new route
76
* @param url - The URL to navigate to
77
* @param as - Optional URL to show in browser
78
* @param options - Navigation options
79
*/
80
push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
81
82
/**
83
* Replace the current route without adding to history
84
* @param url - The URL to navigate to
85
* @param as - Optional URL to show in browser
86
* @param options - Navigation options
87
*/
88
replace(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
89
90
/**
91
* Reload the current route
92
*/
93
reload(): void;
94
95
/**
96
* Navigate back in the browser history
97
*/
98
back(): void;
99
100
/**
101
* Navigate forward in the browser history
102
*/
103
forward(): void;
104
105
/**
106
* Prefetch a route for faster navigation
107
* @param url - The URL to prefetch
108
* @param asPath - Optional URL to show in browser
109
* @param options - Prefetch options
110
*/
111
prefetch(url: Url, asPath?: Url, options?: PrefetchOptions): Promise<void>;
112
113
/**
114
* Set a callback to execute before route changes
115
* @param callback - Callback function receiving route state
116
*/
117
beforePopState(callback: BeforePopStateCallback): void;
118
119
/**
120
* Router events for listening to navigation lifecycle
121
*/
122
events: RouterEvents;
123
}
124
125
interface TransitionOptions {
126
shallow?: boolean;
127
locale?: string | false;
128
scroll?: boolean;
129
}
130
131
interface PrefetchOptions {
132
priority?: boolean;
133
locale?: string | false;
134
}
135
136
type Url = string | UrlObject;
137
138
interface UrlObject {
139
auth?: string | null;
140
hash?: string | null;
141
host?: string | null;
142
hostname?: string | null;
143
href?: string | null;
144
pathname?: string | null;
145
protocol?: string | null;
146
search?: string | null;
147
slashes?: boolean | null;
148
port?: string | number | null;
149
query?: ParsedUrlQuery | null;
150
}
151
152
interface ParsedUrlQuery {
153
[key: string]: string | string[] | undefined;
154
}
155
```
156
157
**Usage Examples**:
158
159
```typescript
160
import { useRouter } from 'next/router';
161
162
export default function Page() {
163
const router = useRouter();
164
165
// Access route information
166
console.log('Pathname:', router.pathname); // "/products/[id]"
167
console.log('Query:', router.query); // { id: "123", search: "shoes" }
168
console.log('As Path:', router.asPath); // "/products/123?search=shoes"
169
170
// Navigate programmatically
171
const handleNavigate = () => {
172
router.push('/about');
173
router.push({ pathname: '/products', query: { category: 'shoes' } });
174
router.push('/products/123', '/products/shoes'); // URL masking
175
};
176
177
// Replace without history
178
const handleReplace = () => {
179
router.replace('/login');
180
};
181
182
// Shallow routing (same page, different query)
183
const handleFilter = (filter: string) => {
184
router.push(
185
{ pathname: router.pathname, query: { ...router.query, filter } },
186
undefined,
187
{ shallow: true }
188
);
189
};
190
191
// Check if router is ready
192
if (!router.isReady) {
193
return <div>Loading...</div>;
194
}
195
196
return (
197
<div>
198
<button onClick={handleNavigate}>Navigate</button>
199
<p>Current ID: {router.query.id}</p>
200
</div>
201
);
202
}
203
```
204
205
### Router Events
206
207
Listen to navigation lifecycle events for loading states and analytics.
208
209
```typescript { .api }
210
interface RouterEvents {
211
/**
212
* Subscribe to router events
213
* @param event - Event name
214
* @param handler - Event handler function
215
*/
216
on(event: string, handler: (...args: any[]) => void): void;
217
218
/**
219
* Unsubscribe from router events
220
* @param event - Event name
221
* @param handler - Event handler function
222
*/
223
off(event: string, handler: (...args: any[]) => void): void;
224
}
225
```
226
227
**Available Events**:
228
- `routeChangeStart(url, { shallow })` - Route change started
229
- `routeChangeComplete(url, { shallow })` - Route change completed
230
- `routeChangeError(err, url, { shallow })` - Route change error
231
- `beforeHistoryChange(url, { shallow })` - Before browser history change
232
- `hashChangeStart(url, { shallow })` - Hash change started
233
- `hashChangeComplete(url, { shallow })` - Hash change completed
234
235
**Usage Examples**:
236
237
```typescript
238
import { useRouter } from 'next/router';
239
import { useEffect } from 'react';
240
241
export default function App({ Component, pageProps }) {
242
const router = useRouter();
243
244
useEffect(() => {
245
const handleStart = (url: string) => {
246
console.log(`Loading: ${url}`);
247
};
248
249
const handleComplete = (url: string) => {
250
console.log(`Loaded: ${url}`);
251
};
252
253
const handleError = (err: Error, url: string) => {
254
console.error(`Error loading ${url}:`, err);
255
};
256
257
router.events.on('routeChangeStart', handleStart);
258
router.events.on('routeChangeComplete', handleComplete);
259
router.events.on('routeChangeError', handleError);
260
261
return () => {
262
router.events.off('routeChangeStart', handleStart);
263
router.events.off('routeChangeComplete', handleComplete);
264
router.events.off('routeChangeError', handleError);
265
};
266
}, [router]);
267
268
return <Component {...pageProps} />;
269
}
270
```
271
272
### GetStaticProps
273
274
Static generation with data fetching at build time. Generates static HTML pages that can be cached by a CDN.
275
276
```typescript { .api }
277
import type { GetStaticProps, GetStaticPropsContext, GetStaticPropsResult } from 'next';
278
279
/**
280
* Data fetching function for static generation
281
* @param context - Context object with params, preview data, and locales
282
* @returns Props and revalidation configuration
283
*/
284
type GetStaticProps<Props = any, Params = ParsedUrlQuery, Preview = any> = (
285
context: GetStaticPropsContext<Params, Preview>
286
) => Promise<GetStaticPropsResult<Props>> | GetStaticPropsResult<Props>;
287
288
interface GetStaticPropsContext<Params = ParsedUrlQuery, Preview = any> {
289
/**
290
* Dynamic route parameters
291
*/
292
params?: Params;
293
294
/**
295
* Preview mode data
296
*/
297
preview?: boolean;
298
previewData?: Preview;
299
300
/**
301
* Current locale
302
*/
303
locale?: string;
304
305
/**
306
* All configured locales
307
*/
308
locales?: string[];
309
310
/**
311
* Default locale
312
*/
313
defaultLocale?: string;
314
315
/**
316
* Revalidation request (ISR)
317
*/
318
revalidateReason?: 'on-demand' | 'stale';
319
}
320
321
type GetStaticPropsResult<Props> =
322
| { props: Props; revalidate?: number | boolean; notFound?: never; redirect?: never }
323
| { props?: never; revalidate?: number | boolean; notFound: true; redirect?: never }
324
| { props?: never; revalidate?: number | boolean; notFound?: never; redirect: Redirect };
325
326
interface Redirect {
327
destination: string;
328
permanent: boolean;
329
statusCode?: number;
330
basePath?: boolean;
331
}
332
```
333
334
**Usage Examples**:
335
336
```typescript
337
import type { GetStaticProps } from 'next';
338
339
interface Post {
340
id: string;
341
title: string;
342
content: string;
343
}
344
345
interface PageProps {
346
post: Post;
347
}
348
349
// Basic usage
350
export const getStaticProps: GetStaticProps<PageProps> = async () => {
351
const post = await fetchPost();
352
353
return {
354
props: {
355
post,
356
},
357
};
358
};
359
360
// With revalidation (ISR)
361
export const getStaticProps: GetStaticProps<PageProps> = async () => {
362
const post = await fetchPost();
363
364
return {
365
props: {
366
post,
367
},
368
revalidate: 60, // Revalidate every 60 seconds
369
};
370
};
371
372
// With dynamic params
373
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
374
const { id } = context.params as { id: string };
375
const post = await fetchPost(id);
376
377
// Return 404 if post not found
378
if (!post) {
379
return {
380
notFound: true,
381
};
382
}
383
384
return {
385
props: {
386
post,
387
},
388
};
389
};
390
391
// With redirect
392
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
393
const post = await fetchPost();
394
395
if (post.draft && !context.preview) {
396
return {
397
redirect: {
398
destination: '/',
399
permanent: false,
400
},
401
};
402
}
403
404
return {
405
props: {
406
post,
407
},
408
};
409
};
410
411
// With preview mode
412
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
413
const post = await fetchPost({
414
preview: context.preview,
415
previewData: context.previewData,
416
});
417
418
return {
419
props: {
420
post,
421
},
422
};
423
};
424
425
export default function Post({ post }: PageProps) {
426
return (
427
<article>
428
<h1>{post.title}</h1>
429
<p>{post.content}</p>
430
</article>
431
);
432
}
433
```
434
435
### GetStaticPaths
436
437
Specify dynamic routes to pre-render at build time. Required for dynamic routes using getStaticProps.
438
439
```typescript { .api }
440
import type { GetStaticPaths, GetStaticPathsContext, GetStaticPathsResult } from 'next';
441
442
/**
443
* Define which dynamic routes to pre-render at build time
444
* @param context - Context object with locales
445
* @returns Paths to pre-render and fallback behavior
446
*/
447
type GetStaticPaths<Params = ParsedUrlQuery> = (
448
context: GetStaticPathsContext
449
) => Promise<GetStaticPathsResult<Params>> | GetStaticPathsResult<Params>;
450
451
interface GetStaticPathsContext {
452
/**
453
* All configured locales
454
*/
455
locales?: string[];
456
457
/**
458
* Default locale
459
*/
460
defaultLocale?: string;
461
}
462
463
interface GetStaticPathsResult<Params = ParsedUrlQuery> {
464
/**
465
* Array of paths to pre-render
466
*/
467
paths: Array<string | { params: Params; locale?: string }>;
468
469
/**
470
* Fallback behavior for paths not in paths array
471
* - false: 404 for non-generated paths
472
* - true: Generate on first request, show loading
473
* - 'blocking': Generate on first request, wait for HTML
474
*/
475
fallback: boolean | 'blocking';
476
}
477
```
478
479
**Usage Examples**:
480
481
```typescript
482
import type { GetStaticPaths, GetStaticProps } from 'next';
483
484
// Basic usage
485
export const getStaticPaths: GetStaticPaths = async () => {
486
const posts = await getAllPosts();
487
488
const paths = posts.map((post) => ({
489
params: { id: post.id },
490
}));
491
492
return {
493
paths,
494
fallback: false, // 404 for non-generated paths
495
};
496
};
497
498
// With fallback: true
499
export const getStaticPaths: GetStaticPaths = async () => {
500
// Only pre-render most popular posts
501
const popularPosts = await getPopularPosts();
502
503
const paths = popularPosts.map((post) => ({
504
params: { id: post.id },
505
}));
506
507
return {
508
paths,
509
fallback: true, // Other posts generated on-demand
510
};
511
};
512
513
export const getStaticProps: GetStaticProps = async (context) => {
514
const { id } = context.params as { id: string };
515
const post = await fetchPost(id);
516
517
if (!post) {
518
return {
519
notFound: true,
520
};
521
}
522
523
return {
524
props: {
525
post,
526
},
527
revalidate: 60,
528
};
529
};
530
531
export default function Post({ post }) {
532
const router = useRouter();
533
534
// Show loading state when fallback: true
535
if (router.isFallback) {
536
return <div>Loading...</div>;
537
}
538
539
return (
540
<article>
541
<h1>{post.title}</h1>
542
<p>{post.content}</p>
543
</article>
544
);
545
}
546
547
// With catch-all routes
548
// pages/docs/[...slug].tsx
549
export const getStaticPaths: GetStaticPaths = async () => {
550
const docs = await getAllDocs();
551
552
const paths = docs.map((doc) => ({
553
params: { slug: doc.path.split('/') },
554
}));
555
556
return {
557
paths,
558
fallback: 'blocking',
559
};
560
};
561
562
// With locales
563
export const getStaticPaths: GetStaticPaths = async ({ locales }) => {
564
const posts = await getAllPosts();
565
566
const paths = posts.flatMap((post) =>
567
locales?.map((locale) => ({
568
params: { id: post.id },
569
locale,
570
})) || [{ params: { id: post.id } }]
571
);
572
573
return {
574
paths,
575
fallback: false,
576
};
577
};
578
```
579
580
### GetServerSideProps
581
582
Server-side rendering with data fetching on each request. Useful for frequently changing data or authenticated content.
583
584
```typescript { .api }
585
import type { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
586
587
/**
588
* Data fetching function for server-side rendering
589
* @param context - Context object with request, response, params, and query
590
* @returns Props or redirect/notFound response
591
*/
592
type GetServerSideProps<Props = any, Params = ParsedUrlQuery, Preview = any> = (
593
context: GetServerSidePropsContext<Params, Preview>
594
) => Promise<GetServerSidePropsResult<Props>>;
595
596
interface GetServerSidePropsContext<Params = ParsedUrlQuery, Preview = any> {
597
/**
598
* HTTP request object
599
*/
600
req: IncomingMessage & {
601
cookies: Partial<{ [key: string]: string }>;
602
};
603
604
/**
605
* HTTP response object
606
*/
607
res: ServerResponse;
608
609
/**
610
* Dynamic route parameters
611
*/
612
params?: Params;
613
614
/**
615
* Query string parameters
616
*/
617
query: ParsedUrlQuery;
618
619
/**
620
* Preview mode data
621
*/
622
preview?: boolean;
623
previewData?: Preview;
624
625
/**
626
* The resolved URL path
627
*/
628
resolvedUrl: string;
629
630
/**
631
* Current locale
632
*/
633
locale?: string;
634
635
/**
636
* All configured locales
637
*/
638
locales?: string[];
639
640
/**
641
* Default locale
642
*/
643
defaultLocale?: string;
644
}
645
646
type GetServerSidePropsResult<Props> =
647
| { props: Props | Promise<Props>; notFound?: never; redirect?: never }
648
| { props?: never; notFound: true; redirect?: never }
649
| { props?: never; notFound?: never; redirect: Redirect };
650
651
interface Redirect {
652
destination: string;
653
permanent: boolean;
654
statusCode?: number;
655
basePath?: boolean;
656
}
657
```
658
659
**Usage Examples**:
660
661
```typescript
662
import type { GetServerSideProps } from 'next';
663
664
interface User {
665
id: string;
666
name: string;
667
email: string;
668
}
669
670
interface PageProps {
671
user: User;
672
}
673
674
// Basic usage
675
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
676
const user = await fetchUser();
677
678
return {
679
props: {
680
user,
681
},
682
};
683
};
684
685
// With authentication
686
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
687
const session = await getSession(context.req, context.res);
688
689
if (!session) {
690
return {
691
redirect: {
692
destination: '/login',
693
permanent: false,
694
},
695
};
696
}
697
698
const user = await fetchUser(session.userId);
699
700
return {
701
props: {
702
user,
703
},
704
};
705
};
706
707
// With dynamic params and query
708
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
709
const { id } = context.params as { id: string };
710
const { tab } = context.query;
711
712
const user = await fetchUser(id);
713
714
if (!user) {
715
return {
716
notFound: true,
717
};
718
}
719
720
return {
721
props: {
722
user,
723
activeTab: tab || 'profile',
724
},
725
};
726
};
727
728
// With cookies
729
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
730
const token = context.req.cookies.token;
731
732
if (!token) {
733
return {
734
redirect: {
735
destination: '/login',
736
permanent: false,
737
},
738
};
739
}
740
741
const user = await fetchUser(token);
742
743
return {
744
props: {
745
user,
746
},
747
};
748
};
749
750
// Set response headers
751
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
752
const user = await fetchUser();
753
754
// Set cache headers
755
context.res.setHeader(
756
'Cache-Control',
757
'public, s-maxage=10, stale-while-revalidate=59'
758
);
759
760
return {
761
props: {
762
user,
763
},
764
};
765
};
766
767
export default function Profile({ user }: PageProps) {
768
return (
769
<div>
770
<h1>{user.name}</h1>
771
<p>{user.email}</p>
772
</div>
773
);
774
}
775
```
776
777
### API Routes
778
779
Create API endpoints within your Next.js application using file-based routing in the pages/api directory.
780
781
```typescript { .api }
782
import type { NextApiRequest, NextApiResponse, NextApiHandler } from 'next';
783
784
/**
785
* HTTP request object for API routes
786
*/
787
interface NextApiRequest extends IncomingMessage {
788
/**
789
* Query string parameters
790
*/
791
query: ParsedUrlQuery;
792
793
/**
794
* Request cookies
795
*/
796
cookies: Partial<{ [key: string]: string }>;
797
798
/**
799
* Parsed request body
800
*/
801
body: any;
802
803
/**
804
* Environment variables
805
*/
806
env: Record<string, string>;
807
808
/**
809
* Preview mode status
810
*/
811
preview?: boolean;
812
813
/**
814
* Preview mode data
815
*/
816
previewData?: any;
817
}
818
819
/**
820
* HTTP response object for API routes
821
*/
822
interface NextApiResponse<Data = any> extends ServerResponse {
823
/**
824
* Set the response status code
825
* @param statusCode - HTTP status code
826
*/
827
status(statusCode: number): NextApiResponse<Data>;
828
829
/**
830
* Send a JSON response
831
* @param body - Response data
832
*/
833
json(body: Data): void;
834
835
/**
836
* Send any response
837
* @param body - Response body
838
*/
839
send(body: Data): void;
840
841
/**
842
* Redirect to another URL
843
* @param statusOrUrl - Status code or URL
844
* @param url - URL if first param is status
845
*/
846
redirect(url: string): void;
847
redirect(status: number, url: string): void;
848
849
/**
850
* Set the response headers
851
* @param name - Header name
852
* @param value - Header value
853
*/
854
setHeader(name: string, value: string | number | string[]): this;
855
856
/**
857
* Revalidate a path (ISR)
858
* @param urlPath - Path to revalidate
859
*/
860
revalidate(urlPath: string): Promise<void>;
861
862
/**
863
* Set preview mode data
864
* @param data - Preview data
865
*/
866
setPreviewData(data: any, options?: {
867
maxAge?: number;
868
path?: string;
869
}): NextApiResponse<Data>;
870
871
/**
872
* Clear preview mode
873
*/
874
clearPreviewData(): NextApiResponse<Data>;
875
}
876
877
/**
878
* API route handler function type
879
* @param req - Request object
880
* @param res - Response object
881
*/
882
type NextApiHandler<Data = any> = (
883
req: NextApiRequest,
884
res: NextApiResponse<Data>
885
) => void | Promise<void>;
886
```
887
888
**Usage Examples**:
889
890
```typescript
891
import type { NextApiRequest, NextApiResponse } from 'next';
892
893
// Basic API route
894
// pages/api/hello.ts
895
export default function handler(
896
req: NextApiRequest,
897
res: NextApiResponse
898
) {
899
res.status(200).json({ message: 'Hello World' });
900
}
901
902
// With type-safe response
903
interface User {
904
id: string;
905
name: string;
906
}
907
908
type ResponseData = User | { error: string };
909
910
export default async function handler(
911
req: NextApiRequest,
912
res: NextApiResponse<ResponseData>
913
) {
914
const user = await fetchUser();
915
916
if (!user) {
917
return res.status(404).json({ error: 'User not found' });
918
}
919
920
res.status(200).json(user);
921
}
922
923
// Handle different HTTP methods
924
export default async function handler(
925
req: NextApiRequest,
926
res: NextApiResponse
927
) {
928
if (req.method === 'GET') {
929
const users = await getUsers();
930
return res.status(200).json(users);
931
}
932
933
if (req.method === 'POST') {
934
const newUser = await createUser(req.body);
935
return res.status(201).json(newUser);
936
}
937
938
if (req.method === 'PUT') {
939
const updatedUser = await updateUser(req.body);
940
return res.status(200).json(updatedUser);
941
}
942
943
if (req.method === 'DELETE') {
944
await deleteUser(req.query.id as string);
945
return res.status(204).end();
946
}
947
948
// Method not allowed
949
res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']);
950
res.status(405).end(`Method ${req.method} Not Allowed`);
951
}
952
953
// Dynamic API route with params
954
// pages/api/posts/[id].ts
955
export default async function handler(
956
req: NextApiRequest,
957
res: NextApiResponse
958
) {
959
const { id } = req.query;
960
961
const post = await getPost(id as string);
962
963
if (!post) {
964
return res.status(404).json({ error: 'Post not found' });
965
}
966
967
res.status(200).json(post);
968
}
969
970
// With authentication
971
export default async function handler(
972
req: NextApiRequest,
973
res: NextApiResponse
974
) {
975
const token = req.cookies.token;
976
977
if (!token) {
978
return res.status(401).json({ error: 'Unauthorized' });
979
}
980
981
const user = await verifyToken(token);
982
983
if (!user) {
984
return res.status(401).json({ error: 'Invalid token' });
985
}
986
987
const data = await getProtectedData(user.id);
988
res.status(200).json(data);
989
}
990
991
// Revalidate on-demand (ISR)
992
export default async function handler(
993
req: NextApiRequest,
994
res: NextApiResponse
995
) {
996
if (req.query.secret !== process.env.REVALIDATE_SECRET) {
997
return res.status(401).json({ message: 'Invalid token' });
998
}
999
1000
try {
1001
await res.revalidate('/posts/1');
1002
return res.json({ revalidated: true });
1003
} catch (err) {
1004
return res.status(500).send('Error revalidating');
1005
}
1006
}
1007
1008
// Preview mode
1009
// pages/api/preview.ts
1010
export default async function handler(
1011
req: NextApiRequest,
1012
res: NextApiResponse
1013
) {
1014
const { secret, slug } = req.query;
1015
1016
if (secret !== process.env.PREVIEW_SECRET) {
1017
return res.status(401).json({ message: 'Invalid token' });
1018
}
1019
1020
const post = await getPreviewPost(slug as string);
1021
1022
if (!post) {
1023
return res.status(401).json({ message: 'Invalid slug' });
1024
}
1025
1026
res.setPreviewData({ postId: post.id });
1027
res.redirect(post.slug);
1028
}
1029
1030
// pages/api/exit-preview.ts
1031
export default async function handler(
1032
req: NextApiRequest,
1033
res: NextApiResponse
1034
) {
1035
res.clearPreviewData();
1036
res.redirect('/');
1037
}
1038
```
1039
1040
### PageConfig
1041
1042
Page-level configuration for API routes and pages to control runtime behavior.
1043
1044
```typescript { .api }
1045
/**
1046
* Configuration options for pages and API routes
1047
*/
1048
interface PageConfig {
1049
/**
1050
* API route specific configuration
1051
*/
1052
api?: {
1053
/**
1054
* Maximum request body size (default: 1mb)
1055
*/
1056
bodyParser?: {
1057
sizeLimit?: string | number;
1058
} | false;
1059
1060
/**
1061
* Maximum response body size in bytes (default: 4mb)
1062
*/
1063
responseLimit?: string | number | false;
1064
1065
/**
1066
* External resolver flag (for custom servers)
1067
*/
1068
externalResolver?: boolean;
1069
};
1070
1071
/**
1072
* Runtime environment: 'nodejs' or 'edge'
1073
*/
1074
runtime?: 'nodejs' | 'edge';
1075
1076
/**
1077
* Disable runtime JS in production
1078
*/
1079
unstable_runtimeJS?: boolean;
1080
1081
/**
1082
* Disable JS preload
1083
*/
1084
unstable_JsPreload?: boolean;
1085
1086
/**
1087
* Maximum execution duration in seconds
1088
*/
1089
maxDuration?: number;
1090
}
1091
```
1092
1093
**Usage Examples**:
1094
1095
```typescript
1096
import type { NextApiRequest, NextApiResponse } from 'next';
1097
1098
// Disable body parsing for file uploads
1099
export const config: PageConfig = {
1100
api: {
1101
bodyParser: false,
1102
},
1103
};
1104
1105
export default async function handler(
1106
req: NextApiRequest,
1107
res: NextApiResponse
1108
) {
1109
// Handle multipart form data manually
1110
const formData = await parseMultipartForm(req);
1111
res.status(200).json({ success: true });
1112
}
1113
1114
// Increase body size limit
1115
export const config: PageConfig = {
1116
api: {
1117
bodyParser: {
1118
sizeLimit: '10mb',
1119
},
1120
},
1121
};
1122
1123
// Edge runtime
1124
export const config: PageConfig = {
1125
runtime: 'edge',
1126
};
1127
1128
export default async function handler(
1129
req: NextApiRequest,
1130
res: NextApiResponse
1131
) {
1132
// Runs on Edge Runtime
1133
return res.json({ message: 'Edge function' });
1134
}
1135
1136
// Increase execution timeout (on supported plans)
1137
export const config: PageConfig = {
1138
maxDuration: 60, // 60 seconds
1139
};
1140
```
1141
1142
### withRouter HOC
1143
1144
Higher-order component to inject the router object into any component.
1145
1146
```typescript { .api }
1147
import { withRouter } from 'next/router';
1148
import type { WithRouterProps } from 'next/router';
1149
1150
/**
1151
* HOC to inject router into component props
1152
* @param Component - Component to wrap with router
1153
* @returns Component with router prop
1154
*/
1155
function withRouter<P extends WithRouterProps>(
1156
Component: React.ComponentType<P>
1157
): React.ComponentType<Omit<P, keyof WithRouterProps>>;
1158
1159
interface WithRouterProps {
1160
router: NextRouter;
1161
}
1162
```
1163
1164
**Usage Examples**:
1165
1166
```typescript
1167
import { withRouter } from 'next/router';
1168
import type { NextRouter } from 'next/router';
1169
1170
// Class component
1171
class MyComponent extends React.Component<{ router: NextRouter }> {
1172
componentDidMount() {
1173
console.log('Current path:', this.props.router.pathname);
1174
}
1175
1176
render() {
1177
return <div>Path: {this.props.router.pathname}</div>;
1178
}
1179
}
1180
1181
export default withRouter(MyComponent);
1182
1183
// Functional component (use useRouter hook instead when possible)
1184
function MyFunctionalComponent({ router }: { router: NextRouter }) {
1185
return (
1186
<div>
1187
<p>Current path: {router.pathname}</p>
1188
<button onClick={() => router.push('/about')}>
1189
Go to About
1190
</button>
1191
</div>
1192
);
1193
}
1194
1195
export default withRouter(MyFunctionalComponent);
1196
```
1197
1198
### Type Utilities
1199
1200
Helper types to infer props from data fetching functions.
1201
1202
```typescript { .api }
1203
import type { InferGetStaticPropsType, InferGetServerSidePropsType } from 'next';
1204
1205
/**
1206
* Infer the props type from a GetStaticProps function
1207
*/
1208
type InferGetStaticPropsType<T extends (...args: any) => any> =
1209
T extends GetStaticProps<infer P> ? P : never;
1210
1211
/**
1212
* Infer the props type from a GetServerSideProps function
1213
*/
1214
type InferGetServerSidePropsType<T extends (...args: any) => any> =
1215
T extends GetServerSideProps<infer P> ? P : never;
1216
```
1217
1218
**Usage Examples**:
1219
1220
```typescript
1221
// Infer props from getStaticProps
1222
export const getStaticProps = async () => {
1223
const posts = await getPosts();
1224
return {
1225
props: {
1226
posts,
1227
timestamp: Date.now(),
1228
},
1229
};
1230
};
1231
1232
type PageProps = InferGetStaticPropsType<typeof getStaticProps>;
1233
// PageProps = { posts: Post[]; timestamp: number }
1234
1235
export default function Page({ posts, timestamp }: PageProps) {
1236
return <div>{posts.length} posts</div>;
1237
}
1238
1239
// Infer props from getServerSideProps
1240
export const getServerSideProps = async (context) => {
1241
const user = await getUser(context.params.id);
1242
return {
1243
props: {
1244
user,
1245
isAdmin: user.role === 'admin',
1246
},
1247
};
1248
};
1249
1250
type ProfileProps = InferGetServerSidePropsType<typeof getServerSideProps>;
1251
// ProfileProps = { user: User; isAdmin: boolean }
1252
1253
export default function Profile({ user, isAdmin }: ProfileProps) {
1254
return <div>{user.name}</div>;
1255
}
1256
```
1257
1258
## Types
1259
1260
```typescript { .api }
1261
interface NextRouter {
1262
pathname: string;
1263
query: ParsedUrlQuery;
1264
asPath: string;
1265
basePath: string;
1266
locale?: string;
1267
locales?: string[];
1268
defaultLocale?: string;
1269
isReady: boolean;
1270
isPreview: boolean;
1271
isFallback: boolean;
1272
push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
1273
replace(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
1274
reload(): void;
1275
back(): void;
1276
forward(): void;
1277
prefetch(url: Url, asPath?: Url, options?: PrefetchOptions): Promise<void>;
1278
beforePopState(callback: BeforePopStateCallback): void;
1279
events: RouterEvents;
1280
}
1281
1282
interface ParsedUrlQuery {
1283
[key: string]: string | string[] | undefined;
1284
}
1285
1286
interface TransitionOptions {
1287
shallow?: boolean;
1288
locale?: string | false;
1289
scroll?: boolean;
1290
}
1291
1292
interface PrefetchOptions {
1293
priority?: boolean;
1294
locale?: string | false;
1295
}
1296
1297
type Url = string | UrlObject;
1298
1299
interface UrlObject {
1300
auth?: string | null;
1301
hash?: string | null;
1302
host?: string | null;
1303
hostname?: string | null;
1304
href?: string | null;
1305
pathname?: string | null;
1306
protocol?: string | null;
1307
search?: string | null;
1308
slashes?: boolean | null;
1309
port?: string | number | null;
1310
query?: ParsedUrlQuery | null;
1311
}
1312
1313
interface Redirect {
1314
destination: string;
1315
permanent: boolean;
1316
statusCode?: number;
1317
basePath?: boolean;
1318
}
1319
1320
type InferGetStaticPropsType<T extends (...args: any) => any> =
1321
T extends GetStaticProps<infer P> ? P : never;
1322
1323
type InferGetServerSidePropsType<T extends (...args: any) => any> =
1324
T extends GetServerSideProps<infer P> ? P : never;
1325
```
1326
1327
## Important Notes
1328
1329
- Pages Router is the legacy routing system; App Router is recommended for new projects
1330
- All Pages Router components are client components by default
1331
- getStaticProps and getStaticPaths only run at build time (or during revalidation)
1332
- getServerSideProps runs on every request
1333
- API routes should not be called from getStaticProps or getServerSideProps (call data layer directly)
1334
- Use ISR (Incremental Static Regeneration) with revalidate for dynamic content that doesn't change frequently
1335
- API routes are not bundled in the client-side code
1336
- API routes can use Node.js modules and don't increase client bundle size
1337