Recipe: Integrating with a Single Page Application (SPA)
Relevant source files
The following files were used as context for generating this wiki page:
This document provides a guide for integrating the consent-manager library into a Single Page Application (SPA). In an SPA environment, content is dynamically loaded and views change without full page reloads. This requires a consent management strategy that can persist state and update the application reactively.
The consent-manager project provides a modular set of tools to handle this. The integration relies on three main components: ConsentManager for core state management, GrantsInterface for user-facing UI elements, and DOMConnector for declarative control over DOM elements. This recipe will cover the initialization, configuration, and specific patterns needed to make these components work effectively within an SPA lifecycle.
Sources: README.md, src/index.ts
Core Components Overview
The library is composed of three main classes that work together to provide a full consent management solution.
| Class | Description | Source File |
|---|---|---|
ConsentManager | The core of the library. It manages consent state, handles cookie storage, and provides an event system for reacting to changes in consent. | src/modules/ConsentManager.ts |
GrantsInterface | Provides pre-built UI components, including a consent banner and a detailed preferences modal, for users to manage their choices. | src/modules/GrantsInterface.ts |
DOMConnector | Connects the consent state to the DOM, allowing elements to be dynamically modified (e.g., loading scripts) based on user grants. | src/modules/DOMConnector.ts |
The following diagram illustrates the relationship between these components. GrantsInterface and DOMConnector are both clients of the central ConsentManager instance.
Initialization and Configuration
In an SPA, the ConsentManager should be instantiated once at the application's entry point to act as a single source of truth for consent state. The GrantsInterface and DOMConnector are then initialized with this central instance.
The sequence diagram below shows the typical initialization flow.
ConsentManager Configuration
The ConsentManager is configured via a configuration object passed to its constructor.
| Parameter | Type | Default | Description |
|---|---|---|---|
version | string | '1' | The version of your consent configuration. If this changes, all existing user consents will be invalidated. |
cookieName | string | 'consent-manager' | The name of the cookie used to store consent preferences. |
expires | number | Date | 'session' | 365 | The cookie's expiration time in days, as a Date object, or 'session'. |
categories | array | [] | An array of consent categories, each with an id, label, description, required, and optional default status. |
Sources: src/modules/ConsentManager.ts:50-65, README.md:26-72
UI Management with GrantsInterface
The GrantsInterface automatically creates and manages a consent banner and a preferences modal. For an SPA, it's often desirable to control the UI programmatically.
To do this, initialize GrantsInterface with autoShow: false. You can then use its methods to show or hide the UI elements in response to application events, such as route changes.
import ConsentManager, { GrantsInterface } from 'consent-manager';
const consentManager = new ConsentManager({ ... });
// Disable automatic banner display
const gi = new GrantsInterface(consentManager, { autoShow: false });
// Manually show the banner when appropriate
gi.showBanner();
Sources: src/modules/GrantsInterface.ts:11, 44-47, 52
The following diagram shows the flow when a user interacts with the UI to grant consent.
Handling Dynamic Content with DOMConnector
The DOMConnector is a powerful tool for declaratively enabling features based on consent. It scans the DOM for elements with a data-consent-manager attribute and modifies them based on the current consent grants.
data-* Attributes
| Attribute | Description | Example |
|---|---|---|
data-consent-manager | A required attribute that registers the element with the DOMConnector. | data-consent-manager |
data-cm-categories | A comma-separated list of category IDs. The element is enabled if consent is granted for any of them. | data-cm-categories="analytics" |
data-cm-attribute | The name of the HTML attribute to set on the element when it's enabled. | data-cm-attribute="src" |
data-cm-value | The value to set for the specified attribute. | data-cm-value="analytics.js" |
data-cm-inverted | Inverts the logic. The attribute is set if consent is not granted. | data-cm-inverted |
Sources: README.md:149-161, src/modules/DOMConnector.ts:10-30
The SPA Challenge: Content Added After Initialization
A key challenge in SPAs is that DOMConnector only scans the document when it is first initialized and when a consent update event is fired. It will not automatically detect new components with data-consent-manager attributes that are rendered dynamically after the initial page load (e.g., during a route change).
The updateDOM method, which performs the scan, is private and cannot be called directly from outside the class.
Sources: src/modules/DOMConnector.ts:5-7, 31-35
Solution: Using the Event System for SPA Components
Given the limitations of DOMConnector with dynamically added content, the most robust solution for an SPA is to use ConsentManager's event system directly within your components.
By listening to the update event, you can create custom logic that runs whenever consent changes. This logic can be integrated into your SPA framework's lifecycle hooks (e.g., useEffect in React, onMounted in Vue) to manage scripts, services, or UI elements that depend on consent. This pattern gives you full control and avoids the limitations of DOMConnector in a dynamic environment.
// In your SPA component
import consentManager from './consentManagerInstance'; // your singleton instance
function handleConsentChange() {
const grants = consentManager.grants;
if (grants.analytics) {
// Initialize analytics script
console.log('Analytics granted, initializing...');
} else {
// Tear down analytics script
console.log('Analytics revoked, disabling...');
}
}
// Run on component mount
handleConsentChange();
// Subscribe to future changes
consentManager.on('update', handleConsentChange);
// Remember to unsubscribe on component unmount
// consentManager.off('update', handleConsentChange);
Sources: src/modules/ConsentManager.ts:158-165
Advanced Integration with the Event System
The ConsentManager provides a simple but powerful event emitter to react to consent changes. This is the recommended way to integrate consent-dependent logic into an SPA.
| Event | Triggered when... | Callback Argument |
|---|---|---|
grant | A category's status changes from false to true. | categoryId |
revoke | A category's status changes from true to false. | categoryId |
update | A category is either granted or revoked. | categoryId |
Sources: src/modules/ConsentManager.ts:46, 150-152
Summary
To successfully integrate consent-manager into an SPA, you should instantiate ConsentManager as a singleton for your application. Use GrantsInterface to handle the user-facing UI, potentially with autoShow: false to gain programmatic control over its visibility.
For handling dynamic content and scripts that depend on consent, the most reliable pattern is to bypass DOMConnector and instead use the ConsentManager's event system (on('update', ...)). By tying your consent-dependent logic to your SPA framework's component lifecycle and the consent-manager events, you can build a robust and reactive integration that correctly handles consent state across all views and user interactions.
Sources: src/modules/ConsentManager.ts, src/modules/DOMConnector.ts, src/modules/GrantsInterface.ts