Skip to content

Blog

Engineering

Why we deprecated offline mode in c15t

c15t 3.2 deprecates offline() and renames it local(), for development and tests only. Here is why consent needs a record that lives outside the browser.

In c15t 3.2, we deprecated offline mode. We renamed offline() to local(), and it's now for development and tests only. We didn't remove it.

Offline mode saved each visitor's consent choice in their browser and nowhere else. You didn't need a backend. So it was the quickest way to get a banner on your site.

But it never kept a record of consent. It was the wrong thing to offer for production.

Here's what changed, why we did it and what to use instead.

What changed

  • We deprecated offline(). It still works as an alias until c15t v4.
  • local() replaces it. It does the same thing. Policy rules resolve in the browser, and choices stay in browser storage.
  • In development builds, local mode logs one warning per page load. It tells you that c15t keeps no server-side record of consent. You won't see it in production builds. And it never blocks the banner or a save.
  • If you decide a browser-only record is enough, like in tests, set acknowledgeNoRecords: true. That hides the warning.
  • The script tag file c15t.offline.js is now c15t.local.js. We still ship the old file as a deprecated alias.
  • c15t setup now labels local mode "Development and tests only, no consent records".
  • In c15t v4, the offline() name goes away. And acknowledgeNoRecords: true becomes required in the local() type.

On an older major? c15t 1.9.0 and 2.4.0 add the same development warning to offline mode. They keep the offline name.

Why we made this change

A consent banner has two jobs. First, it asks your visitor for a choice. Second, it keeps proof of that choice.

Offline mode only did the first job. The choice lived in browser storage on your visitor's device. c15t didn't keep a copy.

So if your visitor cleared their browser, the record was gone. And if someone later asked what a visitor agreed to and when, you had nothing to show them.

Under the General Data Protection Regulation (GDPR), the proof is on you. If your business relies on consent, you have to show it. Here's Article 7(1):

Article 5(2) makes it a general rule. The GDPR calls it accountability:

The European Data Protection Board (EDPB) explains what this means in its Guidelines 05/2020 on consent. They say: "The burden of proof will be on the controller". They also give an online example:

What the GDPR does not say

To be clear, the GDPR doesn't say "use a database". The EDPB says so: "The GDPR does not prescribe exactly how this must be done."

And it isn't illegal to keep consent only in the browser. Some sites keep their own records another way.

But a record that only lives on your visitor's device is weak proof. Your visitor can delete it any time. You can't read it or audit it later.

With offline mode, it was hard or impossible to show consent.

Offline mode also had no geolocation. So every visitor got the same policy.

It looked like a complete setup. It wasn't. We think it was irresponsible for c15t to make that the easy path, so we changed it.

How to move off offline mode

In a React app built with Vite, change the mode and set the backend URL:

// Before
import { offline } from 'c15t/react';
const mode = offline();

// After, for production
import { hosted } from 'c15t/react';
const mode = hosted();

hosted() reads the backend URL from VITE_C15T_BACKEND_URL or VITE_INTH_PROJECT_URL. Other frameworks use their own variable. You'll find each one in the consent modes docs.

Do you use the script tag? Load c15t.js with data-backend-url instead of c15t.offline.js.

Keep local() for tests and demos:

import { local } from 'c15t/react';
const mode = local({ acknowledgeNoRecords: true });

Thank you

Offline mode was our call. So is this change. If it breaks something for you, or you've got questions, open an issue on GitHub.