Tiny, zero depencency reactive consent/cookie banner, weighing in around 4 kB (gzipped).
At the time of writing, Privcy is made by me for use in my projects, which means it may lack features you need, and development is happening on a as-needed basis. You are more than welcome to contribute to Privcy if you want a feature included, or create a issue if something is not working as expected, but in doing so, keep the above in mind.
To get started, install Privcy with your favourite package manager (PNPM, Yarn, NPM, etc):
pnpm add privcyYou can then import the Privcy class:
// index.ts
import Privcy from 'privcy';
new Privcy({
title: 'Privacy',
description: '<p>Your data, your rules. Here\'s what we\'re working with.</p>',
categories: {
analytics: {
name: 'Analytics',
description: 'Helps us understand what\'s working, what\'s not, and what\'s just plain confusing.',
},
social: {
name: 'Social',
description: 'Enables sharing, liking, and pretending you discovered us first.',
},
},
});Alternatively you can add the script straight into your HTML in a script tag:
<script src="https://unpkg.com/privcy@latest/dist/privcy.js"></script>
<script>
new window.Privcy({
title: 'Privacy',
description: '<p>Your data, your rules. Here\'s what we\'re working with.</p>',
categories: {
analytics: {
name: 'Analytics',
description: 'Helps us understand what\'s working, what\'s not, and what\'s just plain confusing.',
},
social: {
name: 'Social',
description: 'Enables sharing, liking, and pretending you discovered us first.',
},
},
});
</script>Then, the scripts you want to control need to be modified with the data-privcy tag:
<script
data-privcy='{
"category": "analytics",
"src": "/path/to/script.js"
}'
></script>This also works with iframes:
<iframe
data-privcy='{
"category": "social",
"src": "https://example.com"
}'
></iframe>To control a inline script, you can omit the src key in data-privcy, but you need to make sure that the script type is set to plain/text:
<script
data-privcy='{
"category": "social"
}'
type="plain/text"
>
console.log('Hello World!')
</script>In cases where a iframe's category is rejected by the user, we sometimes want to display a informational popup alerting the user that the content is not available. This is done by providing a fallback option in the data-privcy attribute:
<iframe
data-privcy='{
"category": "social",
"src": "https://example.com",
"fallback": "/iframe-fallback.html"
}'
></iframe>As shown, the fallback option consists of a url to a html page that will be embedded instead of the src, if the category is rejected. This html page can be static or dynamic and built basically however you want.
If you want to be able to open the settings menu by clicking a button in the iframe, you need to do the following on your fallback page:
<!doctype html>
<html>
<head>
<!-- ... -->
</head>
<body>
<!-- ... -->
<button>Open settings</button>
<script>
if (window.location !== window.parent.location) {
// Open a broadcast channel. Privcy will listen on this channel.
const parent = new BroadcastChannel('privcy:iframe-fallback');
document
.querySelector('button')
.addEventListener('click', () => {
// The message needs to be exactly this.
const message = { displayBanner: true };
// When the button is clicked, the message is posted to the channel.
parent.postMessage(message);
});
}
</script>
</body>
</html>This allows the iframe fallback to trigger opening the Privcy settings panel.
For a complete example, see iframe-fallback.html.
There are scenarios where user consent decisions need to be recorded and stored.
Privcy supports this with the onConsentRecordChange() method, that takes a
callback function that is run when the consent record is set or updated.
The user consent record consists of:
- timestamp
- which categories are allowed/rejected
- hash to track category updates
- the method used (allow all, reject, customize)
However, Privcy does not provide a user or session identifier, but stores only the data displayed above. Instead, this part is left to the integrator. You can either hook in to your own user/session logic, or create and store a randomly generated UUID.
const privcy = new Privcy(config);
privcy.onConsentRecordChange(async (record) => {
const res = await fetch('/api/consent-record', {
method: 'POST',
body: JSON.stringify({
record,
}),
});
// ...
});Currently, SPA support is experimental. You can use the Privcy.reload() method to handle new scripts/iframes after navigation has taken place, for example:
import { StrictMode, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter, useLocation } from 'react-router';
import Privcy from 'privcy';
const privcy = new Privcy({
// ...
});
function App() {
const location = useLocation();
useEffect(() => {
privcy.reload();
}, [location.pathname]);
return (
// ...
);
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
);import barba from '@barba/core';
import Privcy from 'privcy';
const privcy = new Privcy({
// ...
});
barba.init({
transitions: [
{
// ...
after: () => {
privcy.reload();
},
},
],
});Privcy shipps with a minimal default stylesheet, which can be used like so:
<script src="https://unpkg.com/privcy@latest/dist/privcy.js"></script>
<script src="https://unpkg.com/privcy@latest/dist/privcy.css"></script>
<script>
new window.Privcy({
// config ...
});However, to get a banner that matches your site design, you're better off writing your own CSS! Use this template to get started. Since Privcy is essentially only a standard HTML dialog element, you should be able to customize it to your liking.
| Format | Size | gZip |
|---|---|---|
| dist/privcy.mjs (ESM) | 15.85 kB | 4.32 kB |
| dist/privcy.js (IIFE) | 6.70 kB | 2.73 kB |
User privacy and ensuring your analytics and data collection is law-abiding is and will always be your responsibility, the web service owner. Privcy is a tool that's meant to be as light-weight and simple as possible, while giving the user a 'opt-in' propmt. However, as you've surely noticed by now Privcy itself can't, and has no interest in, control the content of your page for you. It only handles the scripts and iframes it has been tasked to handle, nothing more. It's up to you to implement the right strategies to protect your users' privacy.
Copyright (© 2024 - present) Simon Lagerlöf
Licensed under the BSD 3 Clause license.
