@pactconsent/web
v0.3.1
Published
Framework-neutral Pact client, SSR bootstrap renderer, and Web Components
Maintainers
Readme
@pactconsent/web
Framework-neutral Pact integration for raw JavaScript, server-rendered sites, and any framework that accepts Web Components.
It provides three things without depending on React, Vue, Svelte, Angular, or another UI runtime:
renderGoogleConsentModeDefault()for denied Consent Mode v2 defaults before Google tagsrenderPactBootstrap()for safe server-rendered installation before optional tagspactClientfor typed consent state and actions<pact-gate>and<pact-preferences-button>custom elements
Install
pnpm add @pactconsent/web @pactconsent/runtimeThe 398-byte inline bootstrap must appear before optional analytics, advertising, or other purpose-bound code. Serve pact.js from @pactconsent/runtime on the same first-party origin.
Server-rendered bootstrap
import {
renderGoogleConsentModeDefault,
renderPactBootstrap,
} from '@pactconsent/web';
const consentDefaults = renderGoogleConsentModeDefault();
const bootstrap = renderPactBootstrap({
siteKey: 'site_live_123',
src: '/pact.js',
apiBase: '/v1',
});Render consentDefaults, then bootstrap, as trusted HTML at the start of <head>. Both must precede Google Tag Manager, gtag.js, analytics, advertising, and other optional code. Pact starts ad_storage, ad_user_data, ad_personalization, and analytics_storage denied, then emits the matching update before activating a purpose-gated Google tag. Customer-controlled attribute values are escaped. A CSP nonce can be supplied with nonce.
Use basic Consent Mode by making the Google tag inert until Pact allows its purpose:
<script
type="text/plain"
data-pact-purpose="analytics"
data-pact-src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXX">
</script>Raw JavaScript
import { pactClient } from '@pactconsent/web';
const state = await pactClient.getState();
const unsubscribe = await pactClient.onChange((next) => {
console.log(next.purposes);
});
await pactClient.allow(['analytics']);
await pactClient.deny(['marketing']);
await pactClient.save({ analytics: true, marketing: false });
unsubscribe();Web Components
import { definePactElements } from '@pactconsent/web';
definePactElements();<pact-gate purpose="analytics">
<analytics-dashboard></analytics-dashboard>
</pact-gate>
<pact-preferences-button>
Manage privacy
</pact-preferences-button><pact-gate> stays hidden until its known purpose is allowed and reacts to later changes. Unknown purposes remain hidden. The preferences button uses a Shadow DOM button, exposes part="button" for complete styling, and invokes the same Pact preference surface as the managed runtime.
Both elements emit a bubbling, composed pact-error event if the bootstrap or runtime is unavailable. Calling definePactElements() repeatedly is safe.
See the repository's framework integration matrix for React, Next.js, Vue, Nuxt, Angular, Svelte, SvelteKit, Astro, React Router/Remix, Gatsby, SolidStart, and Qwik recipes.
