Cookie consent
Ask readers before analytics runs, with Blume's own banner or a hosted consent manager like Osano or Ethyca, as an adapter in blume.config.ts.
Set consent in blume.config.ts to ask readers before your analytics runs. It takes one adapter from blume/consent: Blume’s own banner, or a hosted consent manager you already use.
import { defineConfig } from "blume";
import { googleAnalytics, posthog } from "blume/analytics";
import { native } from "blume/consent";
export default defineConfig({
analytics: [
googleAnalytics({ id: "G-XXXXXXXXXX" }),
posthog({ key: "phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }),
],
consent: native({ policy: "/privacy" }),
});
With consent set, every analytics adapter waits. Its tags are in the page but don’t run until the reader allows analytics, and then they run in the order you listed them. A reader who declines never loads them, and doesn’t see the page rating or its comment box either, since their answers would have nowhere to go. Vercel Web Analytics loads either way, since its component isn’t a tag Blume can hold, but it sends nothing until the reader allows analytics.
Adapters
| Adapter | Consent manager | Required |
|---|---|---|
native() |
Blume’s own banner | — |
osano() |
Osano | customerId, configId |
ethyca() |
Ethyca | privacyCenter |
Blume’s banner
native() shows a small card at the foot of the page until the reader answers, with Accept and Decline side by side, so declining is as easy as accepting. The answer stays in the reader’s browser, and the banner doesn’t come back once they’ve given it.
consent: native({
policy: "/privacy", // optional: links the banner to your privacy policy
}),
policy takes a page route or a full URL. The banner’s text is translated in every built-in language. To change the wording, override the consent UI strings (message, accept, decline, policy, settings) with i18n.ui.
The banner also works in blume dev, so you can see it while you write. Analytics itself only loads in production builds.
Osano
osano() loads Osano Cookie Consent, which shows the banner and drawer you set up in the Osano dashboard. Blume runs your analytics once Osano reports the reader’s analytics consent, and again whenever they change it. Both IDs come from the script tag Osano gives you: https://cmp.osano.com/<customerId>/<configId>/osano.js.
import { osano } from "blume/consent";
consent: osano({ customerId: "AzZdRbSIoEzZU2", configId: "7b9e2f4a-…" }),
Osano decides what counts as consent in each region, following the compliance mode you chose in its dashboard.
Ethyca
ethyca() loads Ethyca’s Fides consent manager from your privacy center, which shows the banner and modal you set up in Fides. Blume runs your analytics once the reader’s analytics notice is on: opted in, not opted out of, or a notice that only asks them to acknowledge it.
import { ethyca } from "blume/consent";
consent: ethyca({
privacyCenter: "https://privacy.example.com",
propertyId: "FDS-XXXXXX", // optional: which Fides property to load
notice: "analytics", // optional: the notice key that covers analytics
}),
notice defaults to analytics. Set it when your Fides notice for analytics has another key.
Like analytics, Osano and Fides load in production builds only, so testing in blume dev creates no consent records. Preview them with blume build and blume preview.
Changing an answer
When consent is set, the site footer shows a Cookie settings link. It reopens Blume’s banner, or the hosted consent manager’s preferences. If you replace the footer with your own Footer, give any element the data-blume-consent-open attribute to make it the link:
<button data-blume-consent-open type="button">Cookie settings</button>
A reader who allows analytics and later takes it back gets a page reload, the one way to stop scripts that already started. From then on, analytics stays off.
Your own scripts
Hold any script of your own the same way: give it type="text/plain" and data-blume-consent="analytics", and it runs when the reader allows analytics. To react to answers yourself, listen for the blume:consent event on window, whose detail.analytics is the reader’s choice. window.blumeConsent.analytics holds the current one: null until the consent manager answers, then true or false.
window.addEventListener("blume:consent", (event) => {
if (event.detail.analytics) {
// start something that needs consent
}
});