mmoollllee / laravel-consent-control
GDPR cookie consent banner and content blocking for Laravel — server-rendered Blade on top of the framework-agnostic consent-control JS runtime.
Package info
github.com/mmoollllee/laravel-consent-control
pkg:composer/mmoollllee/laravel-consent-control
Requires
- php: ^8.2
- illuminate/contracts: ^11.28|^12.0|^13.0
- illuminate/support: ^11.28|^12.0|^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.18
- orchestra/testbench: ^9.5|^10.0
- pestphp/pest: ^3.5
- pestphp/pest-plugin-laravel: ^3.0
This package is auto-updated.
Last update: 2026-07-15 14:19:10 UTC
README
GDPR cookie-consent banner and content blocking for Laravel — server-rendered Blade
on top of the framework-agnostic consent-control
JavaScript runtime.
This is the Laravel layer of a three-package stack:
| Package | Role |
|---|---|
consent-control (npm) |
Framework-agnostic runtime + CSS (plain HTML, WordPress, any framework). |
laravel-consent-control (this package) |
Blade components, config, drivers, translations, server helpers. |
filament-consent-control |
Optional Filament admin UI (settings form + RichEditor iframe plugin). |
A single config file is all you need — no database, no admin panel required.
Features
- Server-rendered banner (no flash, SEO-friendly) wired by the shared JS runtime
- Configurable consent categories with declarative scripts / inline JS on consent
- Iframe & custom-content blocking via
<x-consent-control-message> - Conditional rendering via
<x-consent-control-gate>(server-side + reactive) configoreloquentdriver — store settings in a file or on your own model- Multilingual (DE/EN, extensible) category labels resolved through
__() - Server-side
ConsentControl::granted('analytics')helper - Cookie format compatible with the
consent-controlnpm package:consentcontrol=necessary|analytics
Requirements
- PHP 8.2+
- Laravel 11.28+ / 12
Installation
composer require mmoollllee/laravel-consent-control
Publish the config and the runtime assets:
php artisan vendor:publish --tag=consent-control-config php artisan vendor:publish --tag=consent-control-assets
Optionally publish the translations to customise them:
php artisan vendor:publish --tag=consent-control-translations
Frontend usage
Render the banner and load the runtime (once per page, e.g. before </body>):
<x-consent-control-banner /> <x-consent-control-scripts />
<x-consent-control-scripts /> ships a standalone stylesheet by default. If you bundle
the CSS yourself (see Assets) disable it with :standalone-css="false".
Block an iframe until consent
<x-consent-control-message consent="functional" src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" src-name="YouTube" :width="560" :height="315" />
Block custom content until consent
<x-consent-control-message consent="functional" type="custom" src-name="OpenStreetMap"> <div id="map" style="height: 400px"></div> </x-consent-control-message>
Conditional content
<x-consent-control-gate consent="analytics"> <p>Only rendered/visible when analytics consent is granted.</p> </x-consent-control-gate> {{-- Or server-side only: --}} @consent('analytics') <x-analytics-widget /> @endconsent
Re-open the banner
<button class="consent-control--open">Cookie settings</button>
Configuration
Everything lives in config/consent-control.php. Labels and descriptions may be plain
strings or translation keys — both are resolved through __(), so the shipped
defaults are multilingual out of the box.
Declare the scripts/JS to run when a category is granted:
'analytics' => [ 'label' => 'Analytics', 'scripts' => [ ['src' => 'https://www.googletagmanager.com/gtag/js?id=G-XXXX', 'async' => true], ], 'inline_script' => "window.dataLayer = window.dataLayer || [];", ],
Consent versioning
Bump version whenever your categories or privacy policy change — visitors whose stored
version differs are re-prompted (their consent is reset):
'version' => env('CONSENT_CONTROL_VERSION', 1), // set null to disable
Block your own scripts
Render inert scripts that activate only on consent:
<x-consent-control-script consent="analytics" src="https://www.googletagmanager.com/gtag/js?id=G-XXXX" /> <x-consent-control-script consent="analytics"> window.dataLayer = window.dataLayer || []; </x-consent-control-script>
Eloquent driver (opt-in)
Store settings as JSON on one of your own models instead of the config file:
CONSENT_CONTROL_DRIVER=eloquent
// config/consent-control.php 'eloquent' => [ 'model' => App\Models\Setting::class, 'field' => 'consent_settings', 'record_id' => 1, ],
// The model needs a JSON cast: protected $casts = ['consent_settings' => 'array'];
Editing that JSON with a ready-made Filament form is what
filament-consent-control adds.
Frontend assets (choose one)
Render the banner and the boot component once per page (e.g. before </body>):
<x-consent-control-banner /> <x-consent-control-scripts :assets="false" />
The banner is designed to inherit your site's design — pick the path that fits your setup:
A) Bundle it yourself — recommended. The runtime JS and the overlay CSS become part of
your Vite build (no extra requests, full control), and Tailwind styles the banner Blade: it
adopts your design tokens (gray scale, the toggle uses your --color-primary) and your
button components (.btn, .btn-primary, .btn-secondary) automatically.
// resources/js/app.js import '../../vendor/mmoollllee/laravel-consent-control/resources/dist/js/consent-control.js';
/* resources/css/app.css */ @import '../../vendor/mmoollllee/laravel-consent-control/resources/dist/css/consent-message.css'; @source '../../vendor/mmoollllee/laravel-consent-control/resources/views/components/**/*.blade.php';
With :assets="false" the component emits only the inline ConsentControl.init(...) boot
config. (Alternatively npm i consent-control and import from node_modules — same runtime.)
B) Published assets — no build step. Publish the runtime and let the component load it:
php artisan vendor:publish --tag=consent-control-assets # → public/vendor/consent-control
<x-consent-control-scripts />
Tailwind projects still add the @source line above and pass :standalone-css="false"
(the Blade styles the banner). Projects without a utility/button system keep the default:
a small neutral stylesheet styles banner + overlay buttons and still adopts your brand where
it can — the accent reads --color-primary / --bs-primary, and every --cc-* variable can
be overridden:
#consent-control-banner, .consent-message { --cc-primary: #b91c1c; }
C) Full control. Publish the views and make them yours — the runtime only needs its
selector contract (#consent-control-banner, input[value=<category>],
#consent-control--submit(-all), .consent-control--open/--close/--reset,
.collapsed-only/.uncollapsed-only and the hide/is-collapsed state classes):
php artisan vendor:publish --tag=consent-control-views
Reopening the banner
Visitors must be able to change their choice later (GDPR). Place a button with the
consent-control--open class anywhere — typically on the privacy policy page; the runtime
binds every such element on init and reopens the banner with the settings expanded:
<button type="button" class="consent-control--open">Cookie-Einstellungen ändern</button>
Components & directives
| Component / directive | Description |
|---|---|
<x-consent-control-banner /> / @consentBanner |
Consent banner |
<x-consent-control-message consent="…" src="…" /> |
Iframe/content with consent overlay |
<x-consent-control-gate consent="…"> |
Slot shown only on consent |
<x-consent-control-script consent="…" src="…" /> |
Consent-gated <script> tag |
<x-consent-control-scripts /> / @consentScripts |
Runtime + boot config |
@consent('analytics') … @endconsent |
Server-side conditional |
JavaScript events
window receives a consent-updated event whenever consent changes:
window.addEventListener('consent-updated', (e) => console.log(e.detail.consents));
License
MIT. See LICENSE.md.