Building a Custom UI
Relevant source files
The following files were used as context for generating this wiki page:The consent-manager library is designed for flexibility, allowing developers to create a fully custom user interface for managing consent preferences. While a pre-built UI is available through the GrantsInterface module, the core logic is handled by the ConsentManager class, which can be used independently. This provides the necessary state, methods, and events to build any consent UI, from a simple banner to a detailed modal.
This guide explains how to use the core ConsentManager API to build a custom UI and how to use the DOMConnector for declarative DOM manipulations based on consent status. It will also analyze the provided GrantsInterface as a practical implementation example.
Sources: README.md:5-8, src/index.ts
Core Component: ConsentManager
The ConsentManager class is the heart of the library. It manages the consent state, handles cookie interactions, and provides an event-driven API for your UI to hook into. It does not render any DOM elements itself, making it the perfect foundation for a custom implementation.
Sources: src/modules/ConsentManager.ts
Key Properties
A ConsentManager instance exposes several public properties that are essential for rendering a UI.
| Property | Type | Description |
|---|---|---|
config | ConsentManagerConfig<G> | The configuration object passed to the constructor, merged with defaults. Contains the categories array. |
grants | Record<G, boolean> | A key-value object where keys are category IDs and values are booleans indicating if consent is granted. |
isCustomized | boolean | true if the user has actively made a choice and a valid, current-version cookie exists. false otherwise. |
Sources: src/modules/ConsentManager.ts:77-79, README.md:50-65
Key Methods & Events
Interaction with the consent state is handled through a simple method and an event system.
setGrant(id: G | '*', status: boolean)
This method is used to update the consent status for one or all categories.
- Calling
setGrant('analytics', true)grants consent for the 'analytics' category. - Calling
setGrant('*', false)revokes consent for all non-required categories. - The method automatically ignores attempts to change the status of
requiredcategories. - After updating the state, it writes the new preferences to a cookie.
Sources: src/modules/ConsentManager.ts:145-171, README.md:69-77
on(eventName, callback) & off(eventName, callback)
The ConsentManager uses an event emitter to announce state changes. This is critical for building a reactive UI that updates automatically.
Available events:
update: Fired whenever a category is granted or revoked. The callback receives the categoryid.grant: Fired only when a category is granted (statusbecomestrue).revoke: Fired only when a category is revoked (statusbecomesfalse).
A custom UI should listen for the update event to re-render its components whenever the consent state changes.
Sources: src/modules/ConsentManager.ts:173-196, README.md:79-93
The following diagram illustrates the class structure of ConsentManager.
This diagram shows the public API of the ConsentManager class, which forms the basis for building a custom UI.
Sources: src/modules/ConsentManager.ts:76-81, 145, 173, 184
Building a Custom UI with ConsentManager
To build a custom UI, you instantiate ConsentManager and use its properties and methods to read and write the consent state.
Step 1: Initialization and Initial Render
First, create an instance of ConsentManager with your specific configuration. Then, use the grants property to perform the initial rendering of your UI elements, such as setting the checked state of checkboxes.
// Example of initializing and rendering a list of toggles
import ConsentManager from 'consent-manager';
const consentManager = new ConsentManager({
version: '2023-01-01',
categories: [
{ id: 'essential', label: 'Essential', description: '...', required: true },
{ id: 'analytics', label: 'Analytics', description: '...', required: false, default: false }
]
});
const container = document.getElementById('custom-consent-ui');
consentManager.config.categories.forEach(category => {
const isChecked = consentManager.grants[category.id];
const isDisabled = category.required;
// Code to create and append a checkbox element to the container
// e.g., checkbox.checked = isChecked; checkbox.disabled = isDisabled;
});
Sources: src/modules/ConsentManager.ts:83-86, src/modules/GrantsInterface.ts:133-144
Step 2: Handling User Input
Attach event listeners to your UI controls (e.g., checkboxes, buttons). When a user interacts with a control, call consentManager.setGrant() to update the consent state.
// checkboxElement is an <input type="checkbox"> for a single category
checkboxElement.addEventListener('change', (event) => {
const categoryId = event.target.dataset.categoryId; // e.g., 'analytics'
const isChecked = event.target.checked;
consentManager.setGrant(categoryId, isChecked);
});
// "Accept All" button
acceptAllButton.addEventListener('click', () => {
consentManager.setGrant('*', true);
});
Sources: src/modules/GrantsInterface.ts:161-166, README.md:72-77
Step 3: Reacting to State Changes
To ensure your UI stays in sync (e.g., if setGrant('*', ...) is called), you must subscribe to the update event. The callback should re-render the relevant parts of your UI based on the new state in consentManager.grants.
function renderUI() {
// Logic to update all checkboxes based on consentManager.grants
document.querySelectorAll('.consent-checkbox').forEach(checkbox => {
const categoryId = checkbox.dataset.categoryId;
checkbox.checked = consentManager.grants[categoryId];
});
}
consentManager.on('update', (updatedCategoryId) => {
console.log(`Category ${updatedCategoryId} was updated!`);
// You can either update the specific element or re-render the whole UI
renderUI();
});
Sources: src/modules/ConsentManager.ts:173-179, src/modules/GrantsInterface.ts:168-172
UI Interaction Flow
The following sequence diagram shows the complete flow from user interaction to UI update.
This diagram illustrates how a user action on the UI triggers a call to ConsentManager, which in turn emits an event that the UI listens to for updates.
Sources: src/modules/ConsentManager.ts:145-171, 198-204, src/modules/GrantsInterface.ts:161-172
Example Implementation: GrantsInterface
The GrantsInterface class is a concrete example of a UI built on top of ConsentManager. Its generateConsentTable method demonstrates the principles described above.
- It iterates through
client.config.categoriesto create a<tr>for each category. - It reads the initial state from
client.grantsto set thecheckedproperty. - It sets the
disabledproperty for required categories. - It adds a
changeevent listener to each checkbox, which callsthis.client.setGrant(). - If
autoUpdateis true, it registers a listener usingthis.client.on('update', ...)to update the checkbox state if it changes elsewhere.
Analyzing this class is a great way to understand how to build your own components.
Sources: src/modules/GrantsInterface.ts:129-182
Declarative UI Updates with DOMConnector
For simpler use cases, the DOMConnector provides a declarative way to modify the DOM based on consent, without writing custom JavaScript event handlers. You can use this to conditionally load scripts or show/hide elements.
To use it, instantiate it with your ConsentManager instance: new DOMConnector(consentManager);.
Sources: src/modules/DOMConnector.ts:4-7, README.md:99-101
DOMConnector works by scanning the DOM for elements with a data-consent-manager attribute and then listening for update events from ConsentManager.
DOMConnector Attributes
| Attribute | Description | Example |
|---|---|---|
data-consent-manager | Required attribute to make the element managed by DOMConnector. | |
data-cm-categories | A comma-separated list of category IDs. The element is "active" if any of these are granted. | analytics,marketing |
data-cm-attribute | The HTML attribute to set on the element when it becomes active. | src |
data-cm-value | The value to set for the specified attribute. | https://analytics.com/script.js |
data-cm-inverted | If present, inverts the logic. The attribute is set when consent is not granted. |
Sources: README.md:103-111, src/modules/DOMConnector.ts:10-29, 33-41
DOMConnector Logic Flow
The following diagram shows how DOMConnector processes an element.
This flow demonstrates the logic DOMConnector uses to decide whether to set or remove the target attribute on a managed element.
Sources: src/modules/DOMConnector.ts:9-45
Conclusion
The consent-manager library provides a powerful and unopinionated core API for managing user consent. Developers can leverage the ConsentManager class to build a completely custom UI by reading its state, calling methods to update it, and listening for events to keep the UI reactive. For simpler needs, the DOMConnector offers a convenient, attribute-based method for dynamically altering the DOM. This modular approach ensures that you can create a consent experience that perfectly matches your project's design and requirements.
Sources: README.md:5-8