Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Cookie Consent Laravel Package

devrabiul/laravel-cookie-consent

A GDPR-compliant cookie consent package for Laravel with customizable banners, granular category controls, RTL and dark mode support, responsive design, and zero frontend dependencies. Install via Composer and publish assets for quick setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require devrabiul/laravel-cookie-consent
php artisan vendor:publish --provider="Devrabiul\CookieConsent\CookieConsentServiceProvider"

Wire it into your layout once — not into individual views. Create or edit resources/views/layouts/app.blade.php:

<head>
    ...
    {!! CookieConsent::styles() !!}
</head>
<body>
    @yield('content')
    {!! CookieConsent::scripts() !!}
</body>

That single pair of calls is the whole "hello world": you get a banner, Accept/Reject/Manage buttons, a preferences modal, and a consent cookie. Ship it in staging before you touch any config.

Where to look first

  1. config/cookie-consent.php — the published config is the real API surface. Read it end to end before reading the README's giant inline options array; the README example is a mirror of config keys, not a separate feature set.
  2. The CookieConsent facade class in vendor/devrabiul/laravel-cookie-consent/src — styles(), scripts(), and translate() are the only methods you'll call daily. Skim the scripts() signature to see which options it accepts and how defaults are merged.
  3. The published assets/views — confirm whether JS/CSS are served locally or fetched from a remote COOKIE_CONSENT_ASSET_URL. This determines your CSP and privacy posture.

First real use case — gate Google Analytics behind the analytics category. Drop this in a partial before CookieConsent::scripts():

<script>
function loadGoogleAnalytics() {
    window.dataLayer = window.dataLayer || [];
    function gtag(){ dataLayer.push(arguments); }
    gtag('js', new Date());
    gtag('config', '{{ config('services.ga.id') }}');
    const s = document.createElement('script');
    s.src = 'https://www.googletagmanager.com/gtag/js?id={{ config('services.ga.id') }}';
    s.async = true;
    document.head.appendChild(s);
}
</script>

Then set 'js_action' => 'loadGoogleAnalytics' on the analytics category in config. Nothing loads until the visitor opts in.

Implementation Patterns

Config-first, options-array second. The README's CookieConsent::scripts(options: [...]) example dumps ~150 lines into your Blade template. Don't do that. Put the values in config/cookie-consent.php, drive the toggles from env, and call CookieConsent::scripts() bare. Your layout stays readable and your ops team can flip COOKIE_CONSENT_ANALYTICS per environment without a deploy of Blade files.

COOKIE_CONSENT_ENABLED=true
COOKIE_CONSENT_ANALYTICS=true
COOKIE_CONSENT_MARKETING=false
COOKIE_CONSENT_PREFERENCES=true
COOKIE_CONSENT_THEME_PRESET=modern-blue

One js_action function per category, all defined in one partial. Keep resources/views/partials/cookie-actions.blade.php holding loadGoogleAnalytics(), loadFacebookPixel(), loadPreferencesFunc(), etc. Include it in the layout, reference the names from config. This makes "which script belongs to which consent category" auditable at a glance — the single most valuable thing you can do for a compliance review.

Server-side awareness of consent. The consent state lives in a client cookie, but you can read it in Laravel to avoid rendering tracking markup at all:

// app/Http/Middleware/ShareConsent.php
public function handle($request, Closure $next)
{
    $prefix = config('laravel-cookie-consent.cookie_prefix', 'Laravel_App');
    $raw = $request->cookie($prefix . '_consent'); // inspect actual cookie name first
    view()->share('consent', $raw ? json_decode(base64_decode($raw), true) : []);
    return $next($request);
}

Confirm the cookie name and encoding by inspecting document.cookie in the browser — the README doesn't document the format, so treat this as reverse-engineering until you verify it in the vendor JS.

Re-opening preferences anywhere. Footer, privacy policy page, account settings:

<a href="#" class="showHideToggleCookiePreferencesModal">Change Cookie Preferences</a>

The class form is preferable to the onclick form — no inline handler, plays nicely with CSP, and you can attach addEventListener for analytics on the click.

Per-environment theming. Use theme_preset for branding and dir="rtl" / theme="dark" on <body> for locale and mode. If your app already computes a dir attribute from app()->getLocale(), the banner inherits it for free — no extra wiring.

Testing. Set COOKIE_CONSENT_ENABLED=false in phpunit.xml. For Dusk tests, assert the banner renders, click Accept, then assert your GA script tag appears:

$this->browse(fn ($browser) => $browser
    ->visit('/')
    ->assertSee('Cookie Disclaimer')
    ->click('@cookie-accept')      // add your own selector via config text
    ->waitFor('script[src*="googletagmanager"]'));

Consent logging for an audit trail. If you need provable records (ICO-friendly), capture the save event in JS and POST to a controller that stores user_id|ip|timestamp|accepted_categories|policy_version. The package gives you the UI and the cookie; it does not give you persisted, tamper-evident records. Budget for this separately — it's the gap between "has a banner" and "is compliant."

Gotchas and Tips

Config key mismatch in the docs. The README references both config/cookie-consent.php and config('laravel-cookie-consent.*'). After publishing, run php artisan config:show cookie-consent (or just ls config/) and use the key that actually exists. Copy-pasting the advanced example verbatim will silently fall through to defaults if the key is wrong — no error, just an unthemed banner and wrong lifetimes.

necessary is documented with js_action => 'loadGoogleAnalytics'. That is almost certainly a copy-paste error in the README. Necessary cookies must never trigger analytics. Audit every js_action in your config and make sure the mapping is the one you'd defend in an audit.

disable_page_interaction defaults to true. That's a full-screen overlay that blocks scrolling and clicking until the user responds — an accessibility and UX problem on content-heavy pages. Set it false unless legal explicitly demands a hard block, and always verify keyboard focus is trapped inside the banner.

COOKIE_CONSENT_ASSET_URL points at a remote host by default. Three consequences: a third-party request on every page load (your own privacy policy should disclose it), a CSP script-src/style-src addition, and a hard local-dev breakage. Set it to null locally and strongly consider self-hosting published assets in production so the cookie consent tool doesn't itself drop a third-party cookie.

cookie_prefix is load-bearing. Changing it invalidates every stored consent and re-prompts your entire user base. Pick something stable and app-specific on day one ('AcmeApp'), not the default 'Laravel_App'. Conversely, this is your lever when you materially change your cookie policy and want re-consent.

reject_lifetime vs cookie_lifetime. Rejections expiring much sooner than acceptances is a deliberate dark-ish pattern — it re-asks rejecters more often. Make it a conscious choice, and be ready to explain it. Consider matching them for a cleaner story.

Don't echo styles()/scripts() more than once per response. Multiple layouts (app, auth, guest), or a component that gets rendered twice, will inject duplicate markup and duplicate JS init. Guard with @once or ensure every layout extends one base layout. Symptoms of a double init: two banners, or a modal that opens then immediately closes.

Full-page caching (Cloudflare, responsecache, Blade page cache) is fine — consent is resolved client-side from the cookie — but never cache a page where you've server-side-rendered consent-conditional tracking markup off the consent view variable. Keep the gating client-side and cacheable, or exclude those responses from the cache.

theme="dark" on <body> and theme_preset => 'dark' fight each other. The preset styles the banner; the body attribute flips the host page's mode detection. Choose one mechanism, otherwise you'll get a dark banner on a light page in some browsers.

CSS bleed with Tailwind/Bootstrap. The package claims zero frontend dependencies, but your framework's preflight/reset can still override the banner's typography and button styles. If the banner looks wrong only in production, check stylesheet order — CookieConsent::styles() needs to come after your app CSS, or scope conflicts with !important overrides in a small local stylesheet.

Upgrading. Never --force the config publish on an existing project; diff the new stub against yours and merge keys manually. The README's key list changes between releases (e.g., theme vs theme_preset), and a blind overwrite silently reverts your customized copy, categories, and policy links.

Extension points, honestly assessed. There are only three: the config file, the js_action JS hooks, and the two DOM entry points (showHideToggleCookiePreferencesModal as class or function). There is no documented event bus, no middleware, no server-side model. If you need consent revocation webhooks, per-user consent records, or geo-targeted banner behavior, you're building those yourself around the cookie — plan the integration with that in mind rather than expecting the package to grow into it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource