Skip to main content

Next.js

Components

Component reference

c15t/next exports ConsentRoot and re-exports every component and hook from c15t/react, so a Next.js app needs one import path. Every component except ConsentTheme ships with 'use client'. You can still render them from a Server Component, as long as every prop you pass is serializable.

ComponentRendersRuns asReference
ConsentRootThe consent runtime, seeded with the state resolveConsent returnedClient ComponentConsentRoot
ConsentBannerThe banner, when the visitor's policy asks for oneClient ComponentConsentBanner
ConsentDialogThe preference center as a modalClient ComponentConsentDialog
ConsentWidgetThe preference center inline in a page, for a privacy routeClient ComponentConsentWidget
ConsentDialogLinkAn unstyled button that opens the dialog, for your footerClient ComponentConsentDialogLink
ConsentDialogTriggerA floating, draggable button that opens the dialog. ConsentDialogTriggerToolbar adds your own actions beside itClient ComponentConsentDialogTrigger
ConsentGateIts children while a category is allowed, a placeholder otherwiseClient ComponentConsentGate
ConsentThemeA <style> element with your theme tokensServer-safeCustomize
DevTools from c15t/next/devtoolsA development panel for consent state, scripts and eventsClient ComponentDevTools

Render every component except ConsentTheme inside ConsentRoot, because they read the runtime ConsentRoot creates. ConsentRoot already renders ConsentProvider, so do not mount both.

Start with ConsentRoot, the banner and the dialog

Most apps start with four components. ConsentRoot holds the runtime, ConsentBanner and ConsentDialog ask for a choice, and a ConsentDialogLink lets visitors reopen their preferences. The quickstart renders them in the root layout, a Server Component:

app/layout.tsx
import {
	ConsentBanner,
	ConsentDialog,
	ConsentDialogLink,
	ConsentRoot,
} from 'c15t/next';
import { resolveConsent } from 'c15t/next/server';
import type { ReactNode } from 'react';

import './globals.css';

const RootLayout = ({ children }: { children: ReactNode }) => (
	<html lang="en">
		<body>
			{/* Not awaited: the page renders while consent resolves. */}
			<ConsentRoot state={resolveConsent()}>
				{children}
				<ConsentBanner />
				<ConsentDialog />
				<footer>
					<ConsentDialogLink>Privacy settings</ConsentDialogLink>
				</footer>
			</ConsentRoot>
		</body>
	</html>
);

export default RootLayout;

ConsentRoot reads c15t.config.ts, so the layout passes it only the state from resolveConsent. The Pages Router guide renders the same components in pages/_app.tsx. Add ConsentWidget, ConsentDialogTrigger or ConsentGate later, where a page needs them.

Server and Client Components

In the App Router, the 'use client' boundary decides what you can pass as a prop:

  • ConsentRoot reads c15t.config.ts in the browser, so a Server Component passes it only state. The state from resolveConsent, or its promise, is plain data and can cross. Put functions such as callbacks in the config, not in props from a Server Component.
  • Banner, dialog, widget, link, trigger and gate can render straight from a Server Component page or layout under ConsentRoot. Strings, numbers, booleans and JSX children cross the boundary. Function props, such as the onSelect handler of a ConsentDialogTriggerToolbar action, need a 'use client' file.
  • ConsentTheme has no 'use client' directive. Render it from a Server Component layout and the theme generator stays out of the browser bundle.
  • DevTools loads only in development. Import it with next/dynamic and ssr: false, as its page shows.

A component that reads consent state with a hook, such as useConsent, is always a Client Component. See hooks.

IAB TCF components

c15t/next does not re-export the IAB TCF components. Import IABProvider, IABConsentBanner and IABConsentDialog from c15t/react/iab, render them inside ConsentRoot next to ConsentBanner and ConsentDialog, and load c15t/next/styles.css and c15t/next/iab/styles.css with styles: false. All three are Client Components. The stock banner and dialog stay closed under an IAB policy. See IAB TCF.

Other entry points

ImportExportsUse
c15t/next/components/consent-dialog-linkConsentDialogLink onlyA module that needs the link without loading the rest of the adapter
c15t/next/headlessHeadless hooksYour own banner and dialog markup. See headless
c15t/next/serverresolveConsent and the other server helpersServer Components and Route Handlers
c15t/next/devtoolsDevToolsDevelopment only

Check the components

Load the site in a private window under a policy that asks for a choice:

  1. The banner shows, and DevTools Network has no requests to your vendors' hosts.
  2. Click the footer's ConsentDialogLink. The dialog opens with every optional category off.
  3. Allow one category and save. Its vendor requests appear, and a ConsentGate for that category mounts its children.
  4. Reload. The banner stays closed, and the link still reopens the dialog.
  5. View the page source. With the awaited layout, a first visit contains an element with data-testid="consent-banner-root", and the page after your choice does not.