CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-next

The React Framework for production-grade applications with server-side rendering, static site generation, and full-stack capabilities

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

pages-router.mddocs/

Pages Router (Legacy)

Complete reference for Next.js Pages Router with data fetching methods, API routes, and navigation.

Note: The Pages Router is the legacy routing system. New projects should use the App Router for better performance and features.

The Pages Router provides traditional file-system based routing with data fetching methods including getStaticProps, getServerSideProps, and API routes for full-stack development.

Capabilities

useRouter Hook

Pages Router navigation hook for programmatic routing and accessing route state. Must be used in Client Components.

import { useRouter } from 'next/router';

/**
 * Returns the Pages Router instance
 * @returns NextRouter with navigation methods and route state
 */
function useRouter(): NextRouter;

interface NextRouter {
  /**
   * The current pathname including dynamic route parameters
   */
  pathname: string;

  /**
   * The query string parsed as an object, including dynamic route parameters
   */
  query: ParsedUrlQuery;

  /**
   * The actual path shown in the browser
   */
  asPath: string;

  /**
   * The base path configured in next.config.js
   */
  basePath: string;

  /**
   * The active locale
   */
  locale?: string;

  /**
   * All configured locales
   */
  locales?: string[];

  /**
   * The default locale
   */
  defaultLocale?: string;

  /**
   * Whether the router is ready and router fields are updated client-side
   */
  isReady: boolean;

  /**
   * Whether the application is in preview mode
   */
  isPreview: boolean;

  /**
   * Whether the current page is a fallback page
   */
  isFallback: boolean;

  /**
   * Navigate to a new route
   * @param url - The URL to navigate to
   * @param as - Optional URL to show in browser
   * @param options - Navigation options
   */
  push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;

  /**
   * Replace the current route without adding to history
   * @param url - The URL to navigate to
   * @param as - Optional URL to show in browser
   * @param options - Navigation options
   */
  replace(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;

  /**
   * Reload the current route
   */
  reload(): void;

  /**
   * Navigate back in the browser history
   */
  back(): void;

  /**
   * Navigate forward in the browser history
   */
  forward(): void;

  /**
   * Prefetch a route for faster navigation
   * @param url - The URL to prefetch
   * @param asPath - Optional URL to show in browser
   * @param options - Prefetch options
   */
  prefetch(url: Url, asPath?: Url, options?: PrefetchOptions): Promise<void>;

  /**
   * Set a callback to execute before route changes
   * @param callback - Callback function receiving route state
   */
  beforePopState(callback: BeforePopStateCallback): void;

  /**
   * Router events for listening to navigation lifecycle
   */
  events: RouterEvents;
}

interface TransitionOptions {
  shallow?: boolean;
  locale?: string | false;
  scroll?: boolean;
}

interface PrefetchOptions {
  priority?: boolean;
  locale?: string | false;
}

type Url = string | UrlObject;

interface UrlObject {
  auth?: string | null;
  hash?: string | null;
  host?: string | null;
  hostname?: string | null;
  href?: string | null;
  pathname?: string | null;
  protocol?: string | null;
  search?: string | null;
  slashes?: boolean | null;
  port?: string | number | null;
  query?: ParsedUrlQuery | null;
}

interface ParsedUrlQuery {
  [key: string]: string | string[] | undefined;
}

Usage Examples:

import { useRouter } from 'next/router';

export default function Page() {
  const router = useRouter();

  // Access route information
  console.log('Pathname:', router.pathname); // "/products/[id]"
  console.log('Query:', router.query); // { id: "123", search: "shoes" }
  console.log('As Path:', router.asPath); // "/products/123?search=shoes"

  // Navigate programmatically
  const handleNavigate = () => {
    router.push('/about');
    router.push({ pathname: '/products', query: { category: 'shoes' } });
    router.push('/products/123', '/products/shoes'); // URL masking
  };

  // Replace without history
  const handleReplace = () => {
    router.replace('/login');
  };

  // Shallow routing (same page, different query)
  const handleFilter = (filter: string) => {
    router.push(
      { pathname: router.pathname, query: { ...router.query, filter } },
      undefined,
      { shallow: true }
    );
  };

  // Check if router is ready
  if (!router.isReady) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <button onClick={handleNavigate}>Navigate</button>
      <p>Current ID: {router.query.id}</p>
    </div>
  );
}

Router Events

Listen to navigation lifecycle events for loading states and analytics.

interface RouterEvents {
  /**
   * Subscribe to router events
   * @param event - Event name
   * @param handler - Event handler function
   */
  on(event: string, handler: (...args: any[]) => void): void;

  /**
   * Unsubscribe from router events
   * @param event - Event name
   * @param handler - Event handler function
   */
  off(event: string, handler: (...args: any[]) => void): void;
}

Available Events:

  • routeChangeStart(url, { shallow }) - Route change started
  • routeChangeComplete(url, { shallow }) - Route change completed
  • routeChangeError(err, url, { shallow }) - Route change error
  • beforeHistoryChange(url, { shallow }) - Before browser history change
  • hashChangeStart(url, { shallow }) - Hash change started
  • hashChangeComplete(url, { shallow }) - Hash change completed

Usage Examples:

import { useRouter } from 'next/router';
import { useEffect } from 'react';

export default function App({ Component, pageProps }) {
  const router = useRouter();

  useEffect(() => {
    const handleStart = (url: string) => {
      console.log(`Loading: ${url}`);
    };

    const handleComplete = (url: string) => {
      console.log(`Loaded: ${url}`);
    };

    const handleError = (err: Error, url: string) => {
      console.error(`Error loading ${url}:`, err);
    };

    router.events.on('routeChangeStart', handleStart);
    router.events.on('routeChangeComplete', handleComplete);
    router.events.on('routeChangeError', handleError);

    return () => {
      router.events.off('routeChangeStart', handleStart);
      router.events.off('routeChangeComplete', handleComplete);
      router.events.off('routeChangeError', handleError);
    };
  }, [router]);

  return <Component {...pageProps} />;
}

GetStaticProps

Static generation with data fetching at build time. Generates static HTML pages that can be cached by a CDN.

import type { GetStaticProps, GetStaticPropsContext, GetStaticPropsResult } from 'next';

/**
 * Data fetching function for static generation
 * @param context - Context object with params, preview data, and locales
 * @returns Props and revalidation configuration
 */
type GetStaticProps<Props = any, Params = ParsedUrlQuery, Preview = any> = (
  context: GetStaticPropsContext<Params, Preview>
) => Promise<GetStaticPropsResult<Props>> | GetStaticPropsResult<Props>;

interface GetStaticPropsContext<Params = ParsedUrlQuery, Preview = any> {
  /**
   * Dynamic route parameters
   */
  params?: Params;

  /**
   * Preview mode data
   */
  preview?: boolean;
  previewData?: Preview;

  /**
   * Current locale
   */
  locale?: string;

  /**
   * All configured locales
   */
  locales?: string[];

  /**
   * Default locale
   */
  defaultLocale?: string;

  /**
   * Revalidation request (ISR)
   */
  revalidateReason?: 'on-demand' | 'stale';
}

type GetStaticPropsResult<Props> =
  | { props: Props; revalidate?: number | boolean; notFound?: never; redirect?: never }
  | { props?: never; revalidate?: number | boolean; notFound: true; redirect?: never }
  | { props?: never; revalidate?: number | boolean; notFound?: never; redirect: Redirect };

interface Redirect {
  destination: string;
  permanent: boolean;
  statusCode?: number;
  basePath?: boolean;
}

Usage Examples:

import type { GetStaticProps } from 'next';

interface Post {
  id: string;
  title: string;
  content: string;
}

interface PageProps {
  post: Post;
}

// Basic usage
export const getStaticProps: GetStaticProps<PageProps> = async () => {
  const post = await fetchPost();

  return {
    props: {
      post,
    },
  };
};

// With revalidation (ISR)
export const getStaticProps: GetStaticProps<PageProps> = async () => {
  const post = await fetchPost();

  return {
    props: {
      post,
    },
    revalidate: 60, // Revalidate every 60 seconds
  };
};

// With dynamic params
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
  const { id } = context.params as { id: string };
  const post = await fetchPost(id);

  // Return 404 if post not found
  if (!post) {
    return {
      notFound: true,
    };
  }

  return {
    props: {
      post,
    },
  };
};

// With redirect
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
  const post = await fetchPost();

  if (post.draft && !context.preview) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    };
  }

  return {
    props: {
      post,
    },
  };
};

// With preview mode
export const getStaticProps: GetStaticProps<PageProps> = async (context) => {
  const post = await fetchPost({
    preview: context.preview,
    previewData: context.previewData,
  });

  return {
    props: {
      post,
    },
  };
};

export default function Post({ post }: PageProps) {
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

GetStaticPaths

Specify dynamic routes to pre-render at build time. Required for dynamic routes using getStaticProps.

import type { GetStaticPaths, GetStaticPathsContext, GetStaticPathsResult } from 'next';

/**
 * Define which dynamic routes to pre-render at build time
 * @param context - Context object with locales
 * @returns Paths to pre-render and fallback behavior
 */
type GetStaticPaths<Params = ParsedUrlQuery> = (
  context: GetStaticPathsContext
) => Promise<GetStaticPathsResult<Params>> | GetStaticPathsResult<Params>;

interface GetStaticPathsContext {
  /**
   * All configured locales
   */
  locales?: string[];

  /**
   * Default locale
   */
  defaultLocale?: string;
}

interface GetStaticPathsResult<Params = ParsedUrlQuery> {
  /**
   * Array of paths to pre-render
   */
  paths: Array<string | { params: Params; locale?: string }>;

  /**
   * Fallback behavior for paths not in paths array
   * - false: 404 for non-generated paths
   * - true: Generate on first request, show loading
   * - 'blocking': Generate on first request, wait for HTML
   */
  fallback: boolean | 'blocking';
}

Usage Examples:

import type { GetStaticPaths, GetStaticProps } from 'next';

// Basic usage
export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await getAllPosts();

  const paths = posts.map((post) => ({
    params: { id: post.id },
  }));

  return {
    paths,
    fallback: false, // 404 for non-generated paths
  };
};

// With fallback: true
export const getStaticPaths: GetStaticPaths = async () => {
  // Only pre-render most popular posts
  const popularPosts = await getPopularPosts();

  const paths = popularPosts.map((post) => ({
    params: { id: post.id },
  }));

  return {
    paths,
    fallback: true, // Other posts generated on-demand
  };
};

export const getStaticProps: GetStaticProps = async (context) => {
  const { id } = context.params as { id: string };
  const post = await fetchPost(id);

  if (!post) {
    return {
      notFound: true,
    };
  }

  return {
    props: {
      post,
    },
    revalidate: 60,
  };
};

export default function Post({ post }) {
  const router = useRouter();

  // Show loading state when fallback: true
  if (router.isFallback) {
    return <div>Loading...</div>;
  }

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// With catch-all routes
// pages/docs/[...slug].tsx
export const getStaticPaths: GetStaticPaths = async () => {
  const docs = await getAllDocs();

  const paths = docs.map((doc) => ({
    params: { slug: doc.path.split('/') },
  }));

  return {
    paths,
    fallback: 'blocking',
  };
};

// With locales
export const getStaticPaths: GetStaticPaths = async ({ locales }) => {
  const posts = await getAllPosts();

  const paths = posts.flatMap((post) =>
    locales?.map((locale) => ({
      params: { id: post.id },
      locale,
    })) || [{ params: { id: post.id } }]
  );

  return {
    paths,
    fallback: false,
  };
};

GetServerSideProps

Server-side rendering with data fetching on each request. Useful for frequently changing data or authenticated content.

import type { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from 'next';

/**
 * Data fetching function for server-side rendering
 * @param context - Context object with request, response, params, and query
 * @returns Props or redirect/notFound response
 */
type GetServerSideProps<Props = any, Params = ParsedUrlQuery, Preview = any> = (
  context: GetServerSidePropsContext<Params, Preview>
) => Promise<GetServerSidePropsResult<Props>>;

interface GetServerSidePropsContext<Params = ParsedUrlQuery, Preview = any> {
  /**
   * HTTP request object
   */
  req: IncomingMessage & {
    cookies: Partial<{ [key: string]: string }>;
  };

  /**
   * HTTP response object
   */
  res: ServerResponse;

  /**
   * Dynamic route parameters
   */
  params?: Params;

  /**
   * Query string parameters
   */
  query: ParsedUrlQuery;

  /**
   * Preview mode data
   */
  preview?: boolean;
  previewData?: Preview;

  /**
   * The resolved URL path
   */
  resolvedUrl: string;

  /**
   * Current locale
   */
  locale?: string;

  /**
   * All configured locales
   */
  locales?: string[];

  /**
   * Default locale
   */
  defaultLocale?: string;
}

type GetServerSidePropsResult<Props> =
  | { props: Props | Promise<Props>; notFound?: never; redirect?: never }
  | { props?: never; notFound: true; redirect?: never }
  | { props?: never; notFound?: never; redirect: Redirect };

interface Redirect {
  destination: string;
  permanent: boolean;
  statusCode?: number;
  basePath?: boolean;
}

Usage Examples:

import type { GetServerSideProps } from 'next';

interface User {
  id: string;
  name: string;
  email: string;
}

interface PageProps {
  user: User;
}

// Basic usage
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
  const user = await fetchUser();

  return {
    props: {
      user,
    },
  };
};

// With authentication
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
  const session = await getSession(context.req, context.res);

  if (!session) {
    return {
      redirect: {
        destination: '/login',
        permanent: false,
      },
    };
  }

  const user = await fetchUser(session.userId);

  return {
    props: {
      user,
    },
  };
};

// With dynamic params and query
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
  const { id } = context.params as { id: string };
  const { tab } = context.query;

  const user = await fetchUser(id);

  if (!user) {
    return {
      notFound: true,
    };
  }

  return {
    props: {
      user,
      activeTab: tab || 'profile',
    },
  };
};

// With cookies
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
  const token = context.req.cookies.token;

  if (!token) {
    return {
      redirect: {
        destination: '/login',
        permanent: false,
      },
    };
  }

  const user = await fetchUser(token);

  return {
    props: {
      user,
    },
  };
};

// Set response headers
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
  const user = await fetchUser();

  // Set cache headers
  context.res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  );

  return {
    props: {
      user,
    },
  };
};

export default function Profile({ user }: PageProps) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

API Routes

Create API endpoints within your Next.js application using file-based routing in the pages/api directory.

import type { NextApiRequest, NextApiResponse, NextApiHandler } from 'next';

/**
 * HTTP request object for API routes
 */
interface NextApiRequest extends IncomingMessage {
  /**
   * Query string parameters
   */
  query: ParsedUrlQuery;

  /**
   * Request cookies
   */
  cookies: Partial<{ [key: string]: string }>;

  /**
   * Parsed request body
   */
  body: any;

  /**
   * Environment variables
   */
  env: Record<string, string>;

  /**
   * Preview mode status
   */
  preview?: boolean;

  /**
   * Preview mode data
   */
  previewData?: any;
}

/**
 * HTTP response object for API routes
 */
interface NextApiResponse<Data = any> extends ServerResponse {
  /**
   * Set the response status code
   * @param statusCode - HTTP status code
   */
  status(statusCode: number): NextApiResponse<Data>;

  /**
   * Send a JSON response
   * @param body - Response data
   */
  json(body: Data): void;

  /**
   * Send any response
   * @param body - Response body
   */
  send(body: Data): void;

  /**
   * Redirect to another URL
   * @param statusOrUrl - Status code or URL
   * @param url - URL if first param is status
   */
  redirect(url: string): void;
  redirect(status: number, url: string): void;

  /**
   * Set the response headers
   * @param name - Header name
   * @param value - Header value
   */
  setHeader(name: string, value: string | number | string[]): this;

  /**
   * Revalidate a path (ISR)
   * @param urlPath - Path to revalidate
   */
  revalidate(urlPath: string): Promise<void>;

  /**
   * Set preview mode data
   * @param data - Preview data
   */
  setPreviewData(data: any, options?: {
    maxAge?: number;
    path?: string;
  }): NextApiResponse<Data>;

  /**
   * Clear preview mode
   */
  clearPreviewData(): NextApiResponse<Data>;
}

/**
 * API route handler function type
 * @param req - Request object
 * @param res - Response object
 */
type NextApiHandler<Data = any> = (
  req: NextApiRequest,
  res: NextApiResponse<Data>
) => void | Promise<void>;

Usage Examples:

import type { NextApiRequest, NextApiResponse } from 'next';

// Basic API route
// pages/api/hello.ts
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.status(200).json({ message: 'Hello World' });
}

// With type-safe response
interface User {
  id: string;
  name: string;
}

type ResponseData = User | { error: string };

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  const user = await fetchUser();

  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }

  res.status(200).json(user);
}

// Handle different HTTP methods
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === 'GET') {
    const users = await getUsers();
    return res.status(200).json(users);
  }

  if (req.method === 'POST') {
    const newUser = await createUser(req.body);
    return res.status(201).json(newUser);
  }

  if (req.method === 'PUT') {
    const updatedUser = await updateUser(req.body);
    return res.status(200).json(updatedUser);
  }

  if (req.method === 'DELETE') {
    await deleteUser(req.query.id as string);
    return res.status(204).end();
  }

  // Method not allowed
  res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']);
  res.status(405).end(`Method ${req.method} Not Allowed`);
}

// Dynamic API route with params
// pages/api/posts/[id].ts
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { id } = req.query;

  const post = await getPost(id as string);

  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }

  res.status(200).json(post);
}

// With authentication
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const token = req.cookies.token;

  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  const user = await verifyToken(token);

  if (!user) {
    return res.status(401).json({ error: 'Invalid token' });
  }

  const data = await getProtectedData(user.id);
  res.status(200).json(data);
}

// Revalidate on-demand (ISR)
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.query.secret !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'Invalid token' });
  }

  try {
    await res.revalidate('/posts/1');
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

// Preview mode
// pages/api/preview.ts
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { secret, slug } = req.query;

  if (secret !== process.env.PREVIEW_SECRET) {
    return res.status(401).json({ message: 'Invalid token' });
  }

  const post = await getPreviewPost(slug as string);

  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' });
  }

  res.setPreviewData({ postId: post.id });
  res.redirect(post.slug);
}

// pages/api/exit-preview.ts
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.clearPreviewData();
  res.redirect('/');
}

PageConfig

Page-level configuration for API routes and pages to control runtime behavior.

/**
 * Configuration options for pages and API routes
 */
interface PageConfig {
  /**
   * API route specific configuration
   */
  api?: {
    /**
     * Maximum request body size (default: 1mb)
     */
    bodyParser?: {
      sizeLimit?: string | number;
    } | false;

    /**
     * Maximum response body size in bytes (default: 4mb)
     */
    responseLimit?: string | number | false;

    /**
     * External resolver flag (for custom servers)
     */
    externalResolver?: boolean;
  };

  /**
   * Runtime environment: 'nodejs' or 'edge'
   */
  runtime?: 'nodejs' | 'edge';

  /**
   * Disable runtime JS in production
   */
  unstable_runtimeJS?: boolean;

  /**
   * Disable JS preload
   */
  unstable_JsPreload?: boolean;

  /**
   * Maximum execution duration in seconds
   */
  maxDuration?: number;
}

Usage Examples:

import type { NextApiRequest, NextApiResponse } from 'next';

// Disable body parsing for file uploads
export const config: PageConfig = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // Handle multipart form data manually
  const formData = await parseMultipartForm(req);
  res.status(200).json({ success: true });
}

// Increase body size limit
export const config: PageConfig = {
  api: {
    bodyParser: {
      sizeLimit: '10mb',
    },
  },
};

// Edge runtime
export const config: PageConfig = {
  runtime: 'edge',
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // Runs on Edge Runtime
  return res.json({ message: 'Edge function' });
}

// Increase execution timeout (on supported plans)
export const config: PageConfig = {
  maxDuration: 60, // 60 seconds
};

withRouter HOC

Higher-order component to inject the router object into any component.

import { withRouter } from 'next/router';
import type { WithRouterProps } from 'next/router';

/**
 * HOC to inject router into component props
 * @param Component - Component to wrap with router
 * @returns Component with router prop
 */
function withRouter<P extends WithRouterProps>(
  Component: React.ComponentType<P>
): React.ComponentType<Omit<P, keyof WithRouterProps>>;

interface WithRouterProps {
  router: NextRouter;
}

Usage Examples:

import { withRouter } from 'next/router';
import type { NextRouter } from 'next/router';

// Class component
class MyComponent extends React.Component<{ router: NextRouter }> {
  componentDidMount() {
    console.log('Current path:', this.props.router.pathname);
  }

  render() {
    return <div>Path: {this.props.router.pathname}</div>;
  }
}

export default withRouter(MyComponent);

// Functional component (use useRouter hook instead when possible)
function MyFunctionalComponent({ router }: { router: NextRouter }) {
  return (
    <div>
      <p>Current path: {router.pathname}</p>
      <button onClick={() => router.push('/about')}>
        Go to About
      </button>
    </div>
  );
}

export default withRouter(MyFunctionalComponent);

Type Utilities

Helper types to infer props from data fetching functions.

import type { InferGetStaticPropsType, InferGetServerSidePropsType } from 'next';

/**
 * Infer the props type from a GetStaticProps function
 */
type InferGetStaticPropsType<T extends (...args: any) => any> =
  T extends GetStaticProps<infer P> ? P : never;

/**
 * Infer the props type from a GetServerSideProps function
 */
type InferGetServerSidePropsType<T extends (...args: any) => any> =
  T extends GetServerSideProps<infer P> ? P : never;

Usage Examples:

// Infer props from getStaticProps
export const getStaticProps = async () => {
  const posts = await getPosts();
  return {
    props: {
      posts,
      timestamp: Date.now(),
    },
  };
};

type PageProps = InferGetStaticPropsType<typeof getStaticProps>;
// PageProps = { posts: Post[]; timestamp: number }

export default function Page({ posts, timestamp }: PageProps) {
  return <div>{posts.length} posts</div>;
}

// Infer props from getServerSideProps
export const getServerSideProps = async (context) => {
  const user = await getUser(context.params.id);
  return {
    props: {
      user,
      isAdmin: user.role === 'admin',
    },
  };
};

type ProfileProps = InferGetServerSidePropsType<typeof getServerSideProps>;
// ProfileProps = { user: User; isAdmin: boolean }

export default function Profile({ user, isAdmin }: ProfileProps) {
  return <div>{user.name}</div>;
}

Types

interface NextRouter {
  pathname: string;
  query: ParsedUrlQuery;
  asPath: string;
  basePath: string;
  locale?: string;
  locales?: string[];
  defaultLocale?: string;
  isReady: boolean;
  isPreview: boolean;
  isFallback: boolean;
  push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
  replace(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
  reload(): void;
  back(): void;
  forward(): void;
  prefetch(url: Url, asPath?: Url, options?: PrefetchOptions): Promise<void>;
  beforePopState(callback: BeforePopStateCallback): void;
  events: RouterEvents;
}

interface ParsedUrlQuery {
  [key: string]: string | string[] | undefined;
}

interface TransitionOptions {
  shallow?: boolean;
  locale?: string | false;
  scroll?: boolean;
}

interface PrefetchOptions {
  priority?: boolean;
  locale?: string | false;
}

type Url = string | UrlObject;

interface UrlObject {
  auth?: string | null;
  hash?: string | null;
  host?: string | null;
  hostname?: string | null;
  href?: string | null;
  pathname?: string | null;
  protocol?: string | null;
  search?: string | null;
  slashes?: boolean | null;
  port?: string | number | null;
  query?: ParsedUrlQuery | null;
}

interface Redirect {
  destination: string;
  permanent: boolean;
  statusCode?: number;
  basePath?: boolean;
}

type InferGetStaticPropsType<T extends (...args: any) => any> =
  T extends GetStaticProps<infer P> ? P : never;

type InferGetServerSidePropsType<T extends (...args: any) => any> =
  T extends GetServerSideProps<infer P> ? P : never;

Important Notes

  • Pages Router is the legacy routing system; App Router is recommended for new projects
  • All Pages Router components are client components by default
  • getStaticProps and getStaticPaths only run at build time (or during revalidation)
  • getServerSideProps runs on every request
  • API routes should not be called from getStaticProps or getServerSideProps (call data layer directly)
  • Use ISR (Incremental Static Regeneration) with revalidate for dynamic content that doesn't change frequently
  • API routes are not bundled in the client-side code
  • API routes can use Node.js modules and don't increase client bundle size

docs

api.md

concepts.md

index.md

pages-router.md

patterns.md

special-files.md

tile.json