laravel-cookie-consent maintained by leobsst
A lightweight Laravel package for handling cookie consent with Google Tag Manager integration. No Livewire required — the banner is a pure Blade component with vanilla JavaScript, making it usable in any Laravel project.
Features
- Zero Livewire dependency — pure Blade component + vanilla JS
- Simple binary consent system (Accept/Refuse)
- Automatic Google Tag Manager integration with Consent Mode v2
- Cookie set client-side (non-HttpOnly, readable by JS)
- Persistent user preferences stored in cookies (1 year by default)
- Tailwind CSS styling with customizable accent color
- Dark mode support
- Multi-language support (EN, FR, IT, ES, DE)
Requirements
- PHP 8.2 or higher
- Laravel 11.9 or higher
- Tailwind CSS
Installation
Step 1: Install the Package
composer require leobsst/laravel-cookie-consent
Step 2: Publish Assets (Required)
php artisan vendor:publish --tag=cookie-consent-assets
This publishes public/vendor/cookie-consent/cookie-consent.js, the JavaScript file that handles cookie writing and Google Tag Manager consent updates.
Step 3: Publish Config File (Recommended)
php artisan vendor:publish --tag=cookie-consent-config
This creates config/cookie-consent.php where you can customize:
- GOOGLE_TAG_MANAGER_ID: Your GTM container ID (e.g.
GTM-XXXXXXX) - LEARN_MORE_LINK: URL or route name for the "Learn more" link (default:
/privacy-policy) - CONSENT_BANNER_VIEW: Override the banner view
- ACCENT_COLOR: Button and link color (default:
#3490dc) - duration: Cookie lifetime in minutes (default: 1 year)
- same_site: SameSite cookie attribute (default:
Lax)
Alternative: Use the Install Command
php artisan cookie-consent:install
This will publish the config file and assets, then ask you to star the repository on GitHub.
Configuration
Tailwind CSS Integration
[!IMPORTANT]
Add the following to your
app.cssso Tailwind processes the banner's utility classes:
@source '../../../../vendor/leobsst/laravel-cookie-consent/resources/views/**/*.blade.php';
Usage
Basic Usage
- Add your Google Tag Manager ID to
.env:
GOOGLE_TAG_MANAGER_ID=GTM-XXXXXXX
- Add the scripts directive and the banner component to your layout:
<!DOCTYPE html>
<html>
<head>
<!-- Google Tag Manager with Consent Mode v2 -->
@cookieConsentScripts
</head>
<body>
<!-- Your content -->
<!-- Cookie Consent Banner -->
<x-cookie-consent::cookie-banner />
</body>
</html>
The banner automatically appears for users who have not yet set their cookie preferences.
Complete Example
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
{{-- Google Tag Manager with Consent Mode --}}
@cookieConsentScripts
</head>
<body>
<div id="app">
{{ $slot }}
</div>
{{-- Cookie Consent Banner --}}
<x-cookie-consent::cookie-banner />
</body>
</html>
How It Works
-
@cookieConsentScripts(or<x-cookie-consent::scripts />) injects the GTM snippet withgtag('consent', 'default', ...). The initial state depends on thecookie_consentcookie already being set (e.g. returning visitors). -
<x-cookie-consent::cookie-banner />renders the banner only when GTM is configured and the user has not yet made a choice. It also injects a smallwindow.CookieConsentconfig object (cookie name, duration, SameSite, secure flag) and the banner's JavaScript inline. -
When the user clicks Accept or Refuse, the bundled
cookie-consent.jsscript:- Writes the
cookie_consentcookie directly in the browser (non-HttpOnly so it remains readable by JS on subsequent page loads) - Calls
gtag('consent', 'update', ...)to update consent in the current GTM session - Hides the banner instantly without a page reload
- Writes the
Checking User Consent
The HandleCookieConsent middleware shares $cookieConsentStatus with all views:
// In a Blade view
@if($cookieConsentStatus === 'full')
{{-- User has accepted all cookies --}}
@endif
You can also read the cookie directly via the $_COOKIE superglobal:
$consent = $_COOKIE['cookie_consent'] ?? null;
// 'full' — accepted
// 'none' — refused
// null — not yet set
[!NOTE] Do not use
request()->cookie('cookie_consent')— Laravel'sEncryptCookiesmiddleware will try to decrypt the value and returnnull, because the cookie is written as plain text by the browser-side JavaScript. Reading it via$_COOKIEbypasses decryption and returns the raw value correctly.
Customizing the Banner
Publish the views to modify them:
php artisan vendor:publish --tag=cookie-consent-views
This copies the views to resources/views/vendor/cookie-consent/. You can also point to a completely custom view via the config:
// config/cookie-consent.php
'CONSENT_BANNER_VIEW' => 'my-theme.cookie-banner',
Your custom view receives these variables from CookieBanner:
| Variable | Type | Description |
|---|---|---|
$loadScript |
bool |
Whether GTM is configured |
$learnMoreLink |
?string |
Resolved URL for the "Learn more" link |
$accentColor |
?string |
CSS color value |
$cookieDuration |
int |
Cookie lifetime in seconds |
$sameSite |
string |
SameSite attribute value |
Translations
The banner uses the locale set in config('app.locale'). Available languages: English (en), French (fr), Italian (it), Spanish (es), German (de).
To publish and customize translation files:
php artisan vendor:publish --tag=cookie-consent-lang
Files are published to resources/lang/vendor/cookie-consent/.
Translation keys:
| Key | Default (EN) |
|---|---|
cookie-consent::translations.description |
Our website uses cookies... |
cookie-consent::translations.question |
Do you accept the use of these cookies? |
cookie-consent::translations.learn_more |
Learn more |
cookie-consent::translations.accept |
Accept |
cookie-consent::translations.refuse |
Deny |
Google Tag Manager Consent Mode
The package implements Google Consent Mode v2.
Default state (user has not yet decided):
{
'functional_storage': 'granted',
'security_storage': 'granted',
'analytics_storage': 'denied',
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied'
}
When user accepts:
{
'functional_storage': 'granted',
'security_storage': 'granted',
'analytics_storage': 'granted',
'ad_storage': 'granted',
'ad_user_data': 'granted',
'ad_personalization': 'granted'
}
When user refuses:
{
'functional_storage': 'granted',
'security_storage': 'granted',
'analytics_storage': 'denied',
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied'
}
Note: On a first-time accept, if
gtagis not yet loaded (GTM script hasn't initialized), the page will reload once so that GTM boots with the correct consent state.
Advanced Configuration
Environment Variables
# Required for Google Tag Manager integration
GOOGLE_TAG_MANAGER_ID=GTM-XXXXXXX
# Optional
COOKIE_LEARN_MORE_LINK=/privacy-policy
COOKIE_CONSENT_BANNER_VIEW=cookie-consent::components.cookie-banner
COOKIE_CONSENT_ACCENT_COLOR=#3490dc
Resetting a User's Consent
use Illuminate\Support\Facades\Cookie;
Cookie::queue(Cookie::forget('cookie_consent'));
FAQ
Does this package require Livewire?
No. As of v2, Livewire is not required. The banner is a Blade component and all consent logic runs in vanilla JavaScript.
Can I use this without Google Tag Manager?
Yes. Simply don't set GOOGLE_TAG_MANAGER_ID. The banner will not appear (it only renders when GTM is configured), but the cookie_consent cookie will still be available for you to use in your own scripts.
How do I style the banner to match my design?
- Use
ACCENT_COLORin the config to change the button/link color. - Publish the views and edit the Tailwind classes.
- Create a completely custom view and point
CONSENT_BANNER_VIEWto it.
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Security
If you find a security vulnerability, please email contact@leobsst.fr instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.