Skip to main content

Getting started

Choose your setup

Find your app in this table

Most apps need one row. Follow its guide from start to finish; each guide links to the alternatives it rules out.

Your appSetupGuide
Next.js App Router with a Next.js serverServer resolves consent from a build-time manifest and streams it to the pageApp Router
Next.js App Router, page must arrive with its bannerAwait consent inside SuspenseStream or await
Next.js with static, ISR, 'use cache' or ensureStatic = 'navigation' pagesBrowser resolves consent on those pagesStatic, ISR and cached pages
Next.js Pages RouterResolve consent in getServerSidePropsPages Router
Next.js with output: 'export'Browser resolves consentStatic export
TanStack Start with a serverRoot loader resolves consent on the serverTanStack Start
TanStack Start on a serverless host, banner may follow the pageRoot loader streams consentTanStack Start rendering
TanStack Start in SPA mode or prerenderedBrowser resolves consentTanStack Start rendering
React with Vite, or another single-page appBrowser resolves consentReact
React Router framework mode, Remix, or another server-rendered React appBrowser resolves consent after hydrationReact rendering
Nuxt with a Nuxt serverNuxt module resolves consent on the server from a build-time manifestNuxt
Nuxt with prerendered or cached routesNuxt server for most pages, the browser on prerendered and cached routesNuxt prerendered and cached routes
Nuxt with nuxt generate on static hostingNuxt module, browser resolves consent from the manifest or /initNuxt static hosting
Nuxt with ssr: falseNuxt module, browser resolves consent from the manifestNuxt single-page app
Vue with ViteVue plugin, browser resolves consentVue
Vue rendered on the server without NuxtVue plugin; the banner mounts after hydration. Use Nuxt for the banner in server HTML.Vue rendering
Astro with static outputBrowser resolves consent through the hosted backendAstro static output
Astro with server output and an adapterMiddleware resolves consent from a build-time manifestAstro server output
Svelte with ViteSvelte provider, browser resolves consentSvelte
SvelteKit with a serverRoot layout load resolves consent on the serverSvelteKit
SvelteKit with adapter-static, prerendered pages or SPA modeBrowser resolves consentSvelteKit rendering
Plain HTML, a CMS, a page builder, or a static site generator such as Hugo or EleventyOne script tag, stock banner includedHTML
JavaScript with a bundler and no UI framework@c15t/browser module, stock banner includedJavaScript
Your own UI, or a framework without an adapter such as SolidHeadless runtimeJavaScript headless

Solid has no c15t adapter. Use the headless runtime and subscribe to it from your components.

The backend supplies policies and stores consent records. Every setup above works with each option; only the URL changes.

OptionUse it when
InthYou want hosted policies and consent records. The guides use this.
Self-hosted c15t backendYou need to run the service and its database yourself.
Offline modeLocal development and tests. Policies live in your code and choices stay in the browser, with no consent records. Not recommended for production environments.

To use Inth, create a project, set its policy rules, add your site's origin to its trusted origins, and copy the backend URL. The guides use the placeholder https://your-project.inth.app; replace it with your project's backend URL, including any path prefix.

Server rendering or browser rendering

Where consent resolves decides what the visitor's first HTML contains.

RenderingWhat the visitor gets
The server resolves consent for each requestThe server reads the visitor's cookie and location headers, so the browser starts with the answer. Gated scripts can run right after hydration, and the banner can be part of the HTML.
The browser resolves consentThe HTML is the same for everyone. After the page loads, the browser requests the policy, then shows the banner or runs gated scripts.

A static or prerendered page cannot contain one visitor's consent, because the same HTML goes to everyone. Static sites still use Inth; the browser talks to it directly. A same-origin /api/c15t route only works where a server runs it.

Pages that are cached and shared between visitors, such as Next.js ISR or 'use cache' routes, need browser resolution for the same reason. Cache the public policy manifest, never a response that contains a visitor's consent.

How the server gets the policy

Use a build-time manifest as the default for supported production builds. manifest() is the default mode in Next.js, TanStack Start, Nuxt, Astro and SvelteKit, so their servers resolve requests from the deployment's snapshot. React, Vue and Svelte single-page apps have no server, so their quickstarts use hosted(): the backend reads each visitor's location from the /init request. They can bundle the manifest for browser resolution too, but a regional policy still needs /init on a first visit unless the page knows the location. The framework quickstarts show the build integration. Consent modes lists every mode, its options and the backend URL variable each framework reads.

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

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.

For deployments that need policy updates without a rebuild, choose a runtime path:

  • Cached manifest, manifest({ source: 'runtime' }). Your server downloads the project's public policy file, caches it, and resolves each visitor locally. Consent saves still go to the backend.
  • /init, hosted(). Each visit asks the backend to resolve the visitor. Simpler, but every page load makes a request to the backend.

Data fetching compares them in detail, including proxies and custom transports.