Request Context
Hooks expose request data to components. Works during SSR and after hydration.
Hooks
import {
useParams,
useQuery,
useHeaders,
useHeader,
useCookies,
useCookie,
useRequest,
usePageContext,
} from '@nestjs-ssr/react/client';Use /client for browser-safe imports. The main export includes server-only code.
useParams — Route parameters:
const { id } = useParams();
// Route: /users/:id → { id: '123' }useQuery — Query string:
const { page, sort } = useQuery();
// URL: /users?page=1&sort=name → { page: '1', sort: 'name' }useHeaders — Allowed headers:
const headers = useHeaders();
// { 'x-tenant-id': 'tenant-123', 'accept-language': 'en-US' }useHeader — Single header:
const tenantId = useHeader('x-tenant-id');useCookies — Allowed cookies:
const cookies = useCookies();
// { theme: 'dark', locale: 'en-US' }useCookie — Single cookie:
const theme = useCookie('theme');useRequest — Full request context:
const { url, method, path, params, query } = useRequest();usePageContext — Everything including custom properties:
const ctx = usePageContext();
// { url, path, params, query, method, user, tenant, ... }Filtering Headers & Cookies
Headers and cookies are not exposed by default. Allowlist explicitly:
RenderModule.forRoot({
allowedHeaders: ['accept-language', 'x-tenant-id'],
allowedCookies: ['theme', 'locale'],
});Everything else stays server-side.
Cookies are read from request.cookies when a parser (cookie-parser, @fastify/cookie) is installed, and from the Cookie header otherwise, so no extra middleware is needed.
Allowed values live in their own bags, lowercased:
const ctx = usePageContext();
ctx.headers['x-tenant-id'];
ctx.cookies.theme;They are bags rather than top-level properties so a header named path cannot shadow the URL, and so an application context key cannot be silently overwritten by an incoming header. useHeader() and useHeaders() read the bag for you.
Credential-bearing headers — authorization, proxy-authorization, cookie, x-api-key, and similar — are refused even if they appear in allowedHeaders. The refusal is logged; the value never is.
Allowed headers are still mirrored as top-level context properties (ctx['x-tenant-id']) as part of the existing API contract. The nested bag is the collision-safe form and both access paths remain supported.
Custom Context Properties
For data like authenticated user, tenant, or feature flags, use the context factory:
RenderModule.forRoot({
context: ({ req }) => ({
user: req.user, // From Passport/JWT
tenant: req.tenant, // From middleware
}),
});Access in components:
const { user, tenant } = usePageContext();Whatever the factory returns is serialized into the page, so it is usually a domain object where a DTO belongs. projectContext is the one place to narrow it, and it applies to HTML hydration, JSON, and navigation segments alike:
RenderModule.forRoot({
context: ({ req }) => ({ user: req.user }),
projectContext: ({ context }) => ({
...context,
user: context.user && { id: context.user.id, name: context.user.name },
}),
});The projected context is validated and size-checked before any response header is written. By default an invalid existing value is diagnosed and still served; representation.limits.mode: 'enforce' fails the request instead. Both context and projectContext receive a signal that aborts on the render deadline or client disconnect.
See Authentication Guide for complete setup.
Typed Hooks
Create typed hooks for your app's context:
// src/lib/ssr-hooks.ts
import { createSSRHooks, RenderContext } from '@nestjs-ssr/react/client';
interface AppContext extends RenderContext {
user?: { id: string; name: string; role: string };
tenant?: { id: string; name: string };
}
export const { usePageContext, useParams, useQuery, useRequest } =
createSSRHooks<AppContext>();
// Convenience hooks
export const useUser = () => usePageContext().user;
export const useTenant = () => usePageContext().tenant;Use throughout your app with full type safety:
import { useUser } from '@/lib/ssr-hooks';
export default function Dashboard() {
const user = useUser(); // Fully typed!
return <h1>Welcome, {user?.name}</h1>;
}