new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Next.js · Guides · all subjects

building/redirects

22 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Shallow routing with window.history

Next.js allows using native window.history.pushState and window.history.replaceState methods to update browser history stack without reloading the page. pushState and replaceState calls integrate into the Next.js Router, allowing sync with usePathname and useSearchParams hooks. This is useful when migrating from strict SPAs like Create React App or Vite that have existing code for shallow routing to update URL state.

Shallow routing example with useSearchParams

In 'use client' component: import useSearchParams from 'next/navigation'; in function, get searchParams with const searchParams = useSearchParams(); create updateSorting function that constructs new URLSearchParams from searchParams.toString(), calls urlSearchParams.set() to set the parameter, then calls window.history.pushState(null, '', `?${urlSearchParams.toString()}`) to update URL without reload.

redirect() must be called outside try/catch blocks

The redirect() function throws an error and should be called outside try/catch blocks, not inside the catch statement.

redirect() accepts absolute URLs for external redirects

The redirect() function accepts absolute URLs and can be used to redirect to external links, not just relative paths.

redirect() function - status codes and context

The redirect() function returns 307 (Temporary Redirect) in most contexts. In Server Actions, it returns 303 (See Other) when JavaScript is unavailable, and uses client-side navigation when JavaScript is available. It can be called in Server Components, Route Handlers, and Server Functions. In Client Components during rendering (not event handlers), redirect() is allowed, but useRouter hook should be used for event handlers instead.

permanentRedirect() function - status code

The permanentRedirect() function returns 308 (Permanent Redirect) in most contexts. In Server Actions, it returns 303 (See Other) when JavaScript is unavailable, and uses client-side navigation when JavaScript is available. It can be called in Server Components, Route Handlers, and Server Functions.

permanentRedirect() accepts absolute URLs for external redirects

The permanentRedirect() function accepts absolute URLs and can be used to redirect to external links.

useRouter hook for client-side navigation

The useRouter hook's push() method can be used to redirect inside event handlers in Client Components. In the App Router, import useRouter from 'next/navigation'. In the Pages Router, import useRouter from 'next/router'. The <Link> component should be preferred when programmatic navigation is not required.

next.config.js redirects() method configuration

The redirects() method in next.config.js returns an array of redirect objects. Each redirect object must have: source (string, the incoming request path), destination (string, the destination path), and permanent (boolean, true for 308 Permanent Redirect, false for 307 Temporary Redirect). The method supports path matching with wildcards (e.g., '/blog/:slug'), and header, cookie, and query matching.

next.config.js redirects() - limit and scale considerations

next.config.js redirects() has a platform-dependent limit. On Vercel, there is a limit of 1,024 redirects. For managing 1000+ redirects, consider creating a custom solution using Proxy instead. The redirects() runs before Proxy in the request lifecycle.

Proxy with NextResponse.redirect() for conditional redirects

Proxy allows you to run code before a request is completed. Using NextResponse.redirect() in Proxy, you can redirect based on conditions (e.g., authentication, session management) or handle large numbers of redirects (1000+). Proxy runs after next.config.js redirects() and before rendering. You can specify custom status codes when calling NextResponse.redirect().

Managing redirects at scale with Proxy and Bloom filter pattern

To manage 1000+ redirects without redeploying, use Proxy with a custom solution. The recommended pattern: (1) Create and store a redirect map in a database (e.g., Vercel Edge Config, Redis) or JSON file with structure {'/old': {'destination': '/new', 'permanent': true}}, (2) Use a Bloom filter to efficiently check if a redirect exists before fetching the actual data, (3) If found in the Bloom filter, forward to a Route Handler/API Route to get the actual redirect entry and return the appropriate status code (308 for permanent, 307 for temporary).

Bloom filter redirect example - redirect data structure

When using a Bloom filter optimization pattern for redirects, the redirect map JSON structure should be: {'/old-path': {'destination': '/new-path', 'permanent': true}}. The status code is determined by the permanent field: 308 if permanent is true, 307 if permanent is false.

Example: redirect() in Server Action after mutation

Example of using redirect() after a database mutation in a Server Action: ```ts 'use server' import { redirect } from 'next/navigation' import { revalidatePath } from 'next/cache' export async function createPost(id: string) { try { // Call database } catch (error) { // Handle errors } revalidatePath('/posts') // Update cached posts redirect(`/post/${id}`) // Navigate to the new post page } ```

Example: permanentRedirect() in Server Action after URL change

Example of using permanentRedirect() after a mutation that changes an entity's canonical URL: ```ts 'use server' import { permanentRedirect } from 'next/navigation' import { revalidateTag } from 'next/cache' export async function updateUsername(username: string, formData: FormData) { try { // Call database } catch (error) { // Handle errors } revalidateTag('username', 'max') // Update all references to the username permanentRedirect(`/profile/${username}`) // Navigate to the new user profile } ```

Example: useRouter in Client Component event handler

Example of using useRouter hook to redirect on button click in a Client Component: ```tsx 'use client' import { useRouter } from 'next/navigation' export default function Page() { const router = useRouter() return ( <button type="button" onClick={() => router.push('/dashboard')}> Dashboard </button> ) } ```

Example: next.config.js redirects with path matching

Example configuration of redirects in next.config.js with basic and wildcard path matching: ```ts import type { NextConfig } from 'next' const nextConfig: NextConfig = { async redirects() { return [ // Basic redirect { source: '/about', destination: '/', permanent: true, }, // Wildcard path matching { source: '/blog/:slug', destination: '/news/:slug', permanent: true, }, ] }, } export default nextConfig ```

Example: Proxy with authentication-based redirect

Example of using Proxy to redirect unauthenticated users to /login: ```ts import { NextResponse, NextRequest } from 'next/server' import { authenticate } from 'auth-provider' export function proxy(request: NextRequest) { const isAuthenticated = authenticate(request) // If the user is authenticated, continue as normal if (isAuthenticated) { return NextResponse.next() } // Redirect to login page if not authenticated return NextResponse.redirect(new URL('/login', request.url)) } export const config = { matcher: '/dashboard/:path*', } ```

Example: Proxy with Edge Config for redirect lookup

Example of using Proxy with Vercel Edge Config to look up and execute redirects: ```ts import { NextResponse, NextRequest } from 'next/server' import { get } from '@vercel/edge-config' type RedirectEntry = { destination: string permanent: boolean } export async function proxy(request: NextRequest) { const pathname = request.nextUrl.pathname const redirectData = await get(pathname) if (redirectData && typeof redirectData === 'string') { const redirectEntry: RedirectEntry = JSON.parse(redirectData) const statusCode = redirectEntry.permanent ? 308 : 307 return NextResponse.redirect(redirectEntry.destination, statusCode) } // No redirect found, continue without redirecting return NextResponse.next() } ```

Example: Proxy with Bloom filter for optimized redirect lookup

Example of using Proxy with a Bloom filter to check for redirects before fetching data: ```ts import { NextResponse, NextRequest } from 'next/server' import { ScalableBloomFilter } from 'bloom-filters' import GeneratedBloomFilter from './redirects/bloom-filter.json' type RedirectEntry = { destination: string permanent: boolean } const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any) export async function proxy(request: NextRequest) { const pathname = request.nextUrl.pathname if (bloomFilter.has(pathname)) { const api = new URL( `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`, request.nextUrl.origin ) try { const redirectData = await fetch(api) if (redirectData.ok) { const redirectEntry: RedirectEntry | undefined = await redirectData.json() if (redirectEntry) { const statusCode = redirectEntry.permanent ? 308 : 307 return NextResponse.redirect(redirectEntry.destination, statusCode) } } } catch (error) { console.error(error) } } return NextResponse.next() } ```

Example: Route Handler for Bloom filter redirect lookup

Example Route Handler that returns redirect entries for the Bloom filter pattern: ```ts import { NextRequest, NextResponse } from 'next/server' import redirects from '@/app/redirects/redirects.json' type RedirectEntry = { destination: string permanent: boolean } export function GET(request: NextRequest) { const pathname = request.nextUrl.searchParams.get('pathname') if (!pathname) { return new Response('Bad Request', { status: 400 }) } const redirect = (redirects as Record<string, RedirectEntry>)[pathname] if (!redirect) { return new Response('No redirect', { status: 400 }) } return NextResponse.json(redirect) } ```

Proxy request lifecycle position

Proxy executes after next.config.js redirects() and before rendering. This means that redirects configured in next.config.js are processed first, then Proxy runs, then the request is rendered.

Give your agent this brain