Skip to main content

Documentation

Documentation

The build fetches your public policy once and bundles it, so the server never fetches it at runtime.

Wrap your Next.js config in withConsentManifest. An existing config object or function goes in as the first argument, unchanged:

next.config.ts
import { withConsentManifest } from 'c15t/next/build';
import type { NextConfig } from 'next';

const nextConfig = {} satisfies NextConfig;

// Reads NEXT_PUBLIC_C15T_BACKEND_URL, like defineConsentConfig.
export default withConsentManifest(nextConfig);

next build, next dev and next typegen fetch ${backendURL}/manifest and write the policy to node_modules/.cache/c15t/, outside your source tree, so there is nothing to keep out of Git. next start serves the built snapshot and fetches no policy. Without a backendURL option, the build reads the backendURL in c15t.config.ts, which defaults to NEXT_PUBLIC_C15T_BACKEND_URL, then NEXT_PUBLIC_INTH_PROJECT_URL, so saves and the snapshot point at the same project.

The wrapper reads c15t.config.ts the way Next.js reads next.config.ts, and skips the fetch for mode: hosted(), mode: offline(), manifest({ snapshot }) and manifest({ source: 'runtime' }), which read no build-time manifest. Those builds never contact the backend. If the file can't be read at build time, for example because it imports something Node can't load, the build warns and fetches as for manifest().

The wrapper also finds c15t.config.ts (or .mts, .js, .mjs) at the project root, next to next.config.ts, and points c15t/generated at the cache, in Turbopack and webpack. ConsentRoot, resolveConsent and the consent route read both without an import. Server code that needs the snapshot itself imports snapshot from c15t/generated. Only server bundles get the snapshot: in a browser bundle, c15t/generated imports server-only, so importing it from a client component fails the build. The wrapper also adds c15t, @c15t/core and @c15t/nextjs to transpilePackages, so Pages Router server bundles see the aliases. A package you list in serverExternalPackages stays external: it reads no c15t.config.ts and fetches the policy at runtime.

If the fetch fails, next build stops. next dev logs a warning instead, and snapshot is undefined, so the server fetches and caches the policy at runtime, as with runtime manifest caching. To let a build continue the same way, set onBuildError: 'runtime', or run it with C15T_ON_BUILD_ERROR=runtime:

next.config.ts
export default withConsentManifest(nextConfig, { onBuildError: 'runtime' });

A missing backend URL counts as a failed fetch. The build skips the fetch and leaves snapshot undefined when the backend URL is relative, or when the config sets output: 'export', which has no server to use the snapshot. With onBuildError: 'fail', a relative URL stops the build. A static export always skips the fetch.

withConsentProps() and createPagesConsentRoute() read the snapshot through the same alias. The wrapper adds c15t, @c15t/core and @c15t/nextjs to transpilePackages for this: the Pages Router leaves dependencies external on the server otherwise, and an external package does not see the alias.

The snapshot is fixed at build time:

  • Rebuild after changing policies, translations or vendors. If your CI caches build output, force a fresh build.
  • Consent choices still go to the backend.

The build reads the backend URL from your public backend URL variable when the config doesn't pass one: NEXT_PUBLIC_C15T_BACKEND_URL in Next.js, NUXT_PUBLIC_C15T_BACKEND_URL in Nuxt, PUBLIC_C15T_BACKEND_URL in Astro, Svelte and SvelteKit, and VITE_C15T_BACKEND_URL in TanStack Start and other Vite apps. Each also reads the matching Inth variable, such as NEXT_PUBLIC_INTH_PROJECT_URL, when the c15t one is unset. See set the backend URL.

The fetch waits at most 10 seconds. When it fails, or no backend URL is set, every framework does the same thing:

CommandDefault when the fetch fails
Production build: next build, vite build, nuxt build, astro buildThe build stops with an error.
Dev: next dev, vite dev, nuxt dev, astro devA warning, and the server fetches the policy at runtime.

Set onBuildError to use one behaviour for both. 'fail' stops dev too. 'runtime' lets a production build finish, and the server fetches the policy at runtime. The C15T_ON_BUILD_ERROR environment variable overrides the option, so you can deploy during a backend outage without a code change:

C15T_ON_BUILD_ERROR=runtime npm run build

Turborepo's strict environment mode hides undeclared variables from tasks, so list C15T_ON_BUILD_ERROR in the build task's passThroughEnv there.

The build skips the fetch, without an error, when it can't use a snapshot, for example when the backend URL is relative. With onBuildError: 'fail', a relative URL stops the build. Consent modes lists every case.

To apply policy edits without a rebuild, set mode: manifest({ source: 'runtime' }) in c15t.config.ts. The server then uses runtime manifest caching. Keep withConsentManifest, which also finds c15t.config.ts.