Search by

elegantly / laravel-cookies-consent

QuentinGab

Cookie consent for Laravel

Package info

github.com/ElegantEngineeringTech/laravel-cookies-consent

pkg:composer/elegantly/laravel-cookies-consent

Fund package maintenance!

ElegantEngineeringTech

Statistics

Installs: 5 796

Dependents: 0

Suggesters: 0

Stars: 26

Open Issues: 0

v4.0.1 2026-10-28 20:28 UTC

This package is auto-updated.

Last update: 2026-10-28 20:28:55 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

laravel-cookies-consent

This package provides a simple yet extremely flexible way to manage cookie consent in your Laravel application.

The default cookie banner design requires Tailwind CSS and Alpine.js, but you can publish the component and customize it with your own stack.

Demo here

Requirements

Backend

  • Laravel

Frontend

The default cookie consent banner included in this package requires:

Installation

You can install the package via Composer:

composer require elegantly/laravel-cookies-consent

You can publish the config file with:

php artisan vendor:publish --tag="cookies-consent-config"

This is the content of the published config file:

return [

    /*
    |--------------------------------------------------------------------------
    | URL Configuration
    |--------------------------------------------------------------------------
    |
    | These values determine the package's API route URLs. Both values are
    | nullable and represent the same concepts as Laravel's routing parameters.
    |
    */

    'url' => [
        'domain' => null,
        'prefix' => 'cookiesconsent',
    ],

    /*
    |--------------------------------------------------------------------------
    | Consent Cookie Configuration
    |--------------------------------------------------------------------------
    |
    | To keep track of the user's preferences, this package stores
    | an anonymized cookie. You do not need to register this cookie in the
    | package's cookie manager as it is done automatically (under "essentials").
    |
    | The duration parameter represents the cookie's lifetime in minutes.
    |
    | The domain parameter, when defined, determines the cookie's activity domain.
    | For multiple sub-domains, prefix your domain with "." (e.g., ".mydomain.com").
    |
    */

    'cookie' => [
        'name' => Str::slug(env('APP_NAME', 'laravel'), '_').'_cookiesconsent',
        'lifetime' => 60 * 24 * 365,
        'domain' => null,
    ],

    /*
    |--------------------------------------------------------------------------
    | Legal Page Configuration
    |--------------------------------------------------------------------------
    |
    | Most cookie notices display a link to a dedicated page explaining
    | the extended cookies usage policy. If your application has such a page,
    | you can add its route name here.
    |
    */

    'policy' => null,

];

Usage

This package covers both backend and frontend cookie consent management.

You can choose to use the package only for backend capabilities or for both.

Backend Usage

In the backend, you will register the cookies and a callback associated with each of them. This callback will be a JavaScript script to run when the consent is granted.

Register Your Cookies

First, you should register all the cookies requiring user consent.

To manage cookies, the package provides a service accessible via the Facade: Elegantly\CookiesConsent\Facades\CookiesConsent.

Cookie registration should be done in middleware to access the app and request context. This also allows you to choose the routes relying on those cookies.

To register your cookies, create a new middleware App\Http\Middleware\RegisterCookiesConsent. In this middleware, call CookiesConsent::register to register groups of cookies.

  • Cookies are always registered in groups.
  • A cookie is defined by its name, lifetime, and an optional description.
  • A cookie group can be defined as required. Such cookies cannot be rejected by the user, which is useful for essential cookies like the session cookie.

For example, all cookies related to "Marketing" can be registered together:

namespace App\Http\Middleware;

use Carbon\CarbonInterval;
use Closure;
use Elegantly\CookiesConsent\CookieDefinition;
use Elegantly\CookiesConsent\CookieGroupDefinition;
use Elegantly\CookiesConsent\Facades\CookiesConsent;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class RegisterCookiesConsent
{
    public function handle(Request $request, Closure $next): Response
    {
        // Register cookies related to the Facebook pixel
        CookiesConsent::register(new CookieGroupDefinition(
            key: 'marketing',
            name: __('cookies-consent::cookies.marketing.name'),
            description: __('cookies-consent::cookies.marketing.description'),
            items: [
                new CookieDefinition(
                    name: '_fbc',
                    lifetime: CarbonInterval::years(2),
                    description: __('cookies-consent::cookies._fbc.description')
                ),
                new CookieDefinition(
                    name: '_fbp',
                    lifetime: CarbonInterval::years(3),
                    description: __('cookies-consent::cookies._fbp.description')
                ),
            ],
            onAccepted: function () {
                return <<<'JS'
                    if (typeof fbq === 'function') {
                        fbq('consent', 'grant');
                    }
                JS;
            },
        ));

        return $next($request);
    }
}

Registering Essential Cookies

The package provides a preset for essential cookies. Essential cookies are those that cannot be removed without compromising the application. By default, Laravel includes 2 essential cookies:

  • XSRF-TOKEN
  • Session cookie

This package adds a third one:

  • Consents (a cookie to store consents).

You can automatically register these three essential cookies using:

use Elegantly\CookiesConsent\Facades\CookiesConsent;

CookiesConsent::registerEssentials()
    ->register(
        // ... custom cookie definition
    )

Registering Cookie Callbacks

Using the onAccepted parameter, you can define the JavaScript code to execute when consent is granted to a specific cookie group.

In the previous example, we grant consent using the Facebook pixel.

use Elegantly\CookiesConsent\Facades\CookiesConsent;

CookiesConsent::register(new CookieGroupDefinition(
    // ...
    onAccepted: function () {
        return <<<'JS'
            // This JavaScript code will be executed when consent is granted
            if (typeof fbq === 'function') {
                fbq('consent', 'grant');
            }
        JS;
    },
));

Frontend Usage

Using the Default Cookie Banner

You can use the default cookie banner included with this package. It requires Alpine.js and Tailwind CSS. Cookies are read and written using the browser's native document.cookie API.

Alpine.js Requirement

The default banner implementation requires Alpine.js for reactivity. Ensure it is included in your page.

Simply put the banner component <x-cookies-consent::banner /> at the end of your HTML page, and you are ready to go!

    <!-- ... -->
    <x-cookies-consent::banner />
</body>

JavaScript Requirement

The banner relies on the CookiesConsent JavaScript class included in this package. It reads and writes the consent cookie and runs your onAccepted callbacks.

Add @cookiesConsentScripts to your <head>:

<head>
    <!-- ... -->
    @cookiesConsentScripts
</head>

It inlines the compiled class (about 1 kB gzipped) and creates window.cookiesConsent, an instance configured with your registered cookie groups and the cookie config. There is nothing to build or publish. Register your groups before the <head> renders, for example in a middleware as shown above.

If your application uses a Content Security Policy, the directive reuses the nonce set with Vite::useCspNonce(). You can also pass one yourself: @cookiesConsentScripts(nonce: $nonce).

Tailwindcss Requirement

The default banner is based on elegantly/blade-kit which is styled with tailwindcss. You must then add the following paths to your tailwind app.css.

@import "../../vendor/elegantly/blade-kit/resources/css/styles.css";
@source "../../vendor/elegantly/blade-kit/resources/views/**/*.blade.php";
@source "../../vendor/elegantly/laravel-cookies-consent/resources/views/**/*.blade.php;

Customizing the Default Component

You can customize the default component by publishing the views:

php artisan vendor:publish --tag="cookies-consent-views"

If you previously published the banner, update cookie loops from @foreach ($group as $cookie) to @foreach ($group->items as $cookie).

Using a Custom Component

You can design your own frontend cookie banner.

To retrieve all the cookie definitions, simply call:

use Elegantly\CookiesConsent\Facades\CookiesConsent;

CookiesConsent::getDefinition();

Your component can rely on the window.cookiesConsent instance created by @cookiesConsentScripts, just like the default banner:

const consent = window.cookiesConsent;

if (consent.isPending()) {
    // Display your banner, then call consent.acceptAll(), consent.acceptEssentials()
    // or consent.set("analytics", true).save()
} else {
    consent.runCallbacks();
}

The TypeScript source of the class lives in vendor/elegantly/laravel-cookies-consent/resources/ts, if you want to import it or its types.

Google Tag (gtag.js) Consent Mode

The Google tag powers Google Analytics and Google Ads. Its consent mode lets you deny storage by default and grant it once the user accepts.

Example

This example maps the analytics group to analytics_storage, and the marketing group to ad_storage, ad_user_data and ad_personalization.

1. Initialize consent from the stored choice

In your <head>, before the Google tag, set the default consent from the choice stored in the consent cookie. Groups the user hasn't accepted, including on their first visit, default to denied:

<script>
    window.dataLayer = window.dataLayer || [];

    function gtag() {
        dataLayer.push(arguments);
    }

    {
        const analytics = window.cookiesConsent.hasConsent('analytics') ? 'granted' : 'denied';
        const marketing = window.cookiesConsent.hasConsent('marketing') ? 'granted' : 'denied';

        gtag('consent', 'default', {
            'ad_storage': marketing,
            'ad_user_data': marketing,
            'ad_personalization': marketing,
            'analytics_storage': analytics,
        });
    }
</script>

<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ $gtagId }}"></script>
<script>
    gtag('js', new Date());
    gtag('config', '{{ $gtagId }}');
</script>

This assumes @cookiesConsentScripts is placed above it, so window.cookiesConsent is defined.

The banner only runs your onAccepted callbacks once Alpine.js starts, after the Google tag has sent its first hits. Reading the stored choice here ensures those hits use the right consent.

2. Grant consent

In your middleware, register the analytics and marketing groups and update the consent in their onAccepted callbacks. They run as soon as the user accepts a group in the banner:

use Elegantly\CookiesConsent\Facades\CookiesConsent;
use Elegantly\CookiesConsent\CookieGroupDefinition;
use Elegantly\CookiesConsent\CookieDefinition;
use Carbon\CarbonInterval;

CookiesConsent::register(new CookieGroupDefinition(
    key: 'analytics',
    name: __('cookies-consent::cookies.analytics.name'),
    description: __('cookies-consent::cookies.analytics.description'),
    items: [
        new CookieDefinition(
            name: '_ga',
            lifetime: CarbonInterval::years(2),
            description: __('cookies-consent::cookies._ga.description')
        ),
        new CookieDefinition(
            name: '_ga_ID',
            lifetime: CarbonInterval::years(2),
            description: __('cookies-consent::cookies._ga_ID.description')
        ),
    ],
    onAccepted: function () {
        return <<<'JS'
                gtag('consent', 'update', {
                    'analytics_storage': 'granted',
                });
            JS;
    },
))->register(new CookieGroupDefinition(
    key: 'marketing',
    name: __('cookies-consent::cookies.marketing.name'),
    description: __('cookies-consent::cookies.marketing.description'),
    items: [
        new CookieDefinition(
            name: '_gcl_au',
            lifetime: CarbonInterval::days(90),
            description: 'Used by Google Ads to measure conversions.'
        ),
    ],
    onAccepted: function () {
        return <<<'JS'
                gtag('consent', 'update', {
                    'ad_storage': 'granted',
                    'ad_user_data': 'granted',
                    'ad_personalization': 'granted',
                });
            JS;
    },
));

Meta Pixel (fbq) Consent

The Meta Pixel measures conversions from your Facebook and Instagram ads. Its consent API pauses the Pixel with fbq('consent', 'revoke') until you call fbq('consent', 'grant').

This guide covers the Pixel, which runs in the browser and sets cookies. Meta's Conversions API sends events from your server instead.

Example

This example maps the marketing group to the Pixel.

1. Revoke consent unless it was granted

In your <head>, revoke consent before fbq('init', ...) unless the user has accepted the marketing group:

<!-- Meta Pixel Code -->
<script>
    !function(f,b,e,v,n,t,s)
    {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
    n.callMethod.apply(n,arguments):n.queue.push(arguments)};
    if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
    n.queue=[];t=b.createElement(e);t.async=!0;
    t.src=v;s=b.getElementsByTagName(e)[0];
    s.parentNode.insertBefore(t,s)}(window, document,'script',
    'https://connect.facebook.net/en_US/fbevents.js');

    if (!window.cookiesConsent.hasConsent('marketing')) {
        fbq('consent', 'revoke');
    }

    fbq('init', '{{ $pixelId }}');
    fbq('track', 'PageView');
</script>
<!-- End Meta Pixel Code -->

This assumes @cookiesConsentScripts is placed above it, so window.cookiesConsent is defined.

Leave out the <noscript> image from Meta's snippet: it sends a PageView without running any JavaScript, so without the user's consent.

2. Grant consent

In your middleware, register the marketing group and grant consent in its onAccepted callback. It runs as soon as the user accepts the group in the banner:

use Elegantly\CookiesConsent\Facades\CookiesConsent;
use Elegantly\CookiesConsent\CookieGroupDefinition;
use Elegantly\CookiesConsent\CookieDefinition;
use Carbon\CarbonInterval;

CookiesConsent::register(new CookieGroupDefinition(
    key: 'marketing',
    name: __('cookies-consent::cookies.marketing.name'),
    description: __('cookies-consent::cookies.marketing.description'),
    items: [
        new CookieDefinition(
            name: '_fbc',
            lifetime: CarbonInterval::days(90),
            description: __('cookies-consent::cookies._fbc.description')
        ),
        new CookieDefinition(
            name: '_fbp',
            lifetime: CarbonInterval::days(90),
            description: __('cookies-consent::cookies._fbp.description')
        ),
    ],
    onAccepted: function () {
        return <<<'JS'
                fbq('consent', 'grant');
            JS;
    },
));

TikTok Pixel (ttq) Consent

The TikTok Pixel measures conversions from your TikTok ads. Its consent mode queues events after ttq.holdConsent(), and sends them, including the queued ones, once you call ttq.grantConsent().

Example

This example maps the marketing group to the Pixel.

1. Hold consent unless it was granted

In your <head>, start from the Pixel base code given by TikTok Events Manager and make two changes:

  1. Add "holdConsent", "revokeConsent" and "grantConsent" to the ttq.methods array, so these methods are queued until the Pixel loads.
  2. Hold consent before ttq.load(...) unless the user has accepted the marketing group.
<!-- TikTok Pixel Code -->
<script>
    !function (w, d, t) {
        w.TiktokAnalyticsObject=t;var ttq=w[t]=w[t]||[];ttq.methods=["page","track", /* ... */ ,"holdConsent","revokeConsent","grantConsent"], /* ... the rest of the base code */

        if (!window.cookiesConsent.hasConsent('marketing')) {
            ttq.holdConsent();
        }

        ttq.load('{{ $pixelId }}');
        ttq.page();
    }(window, document, 'ttq');
</script>
<!-- End TikTok Pixel Code -->

This assumes @cookiesConsentScripts is placed above it, so window.cookiesConsent is defined.

2. Grant consent

In your middleware, register the marketing group and grant consent in its onAccepted callback. The PageView queued before the user accepts is sent then:

use Elegantly\CookiesConsent\Facades\CookiesConsent;
use Elegantly\CookiesConsent\CookieGroupDefinition;
use Elegantly\CookiesConsent\CookieDefinition;
use Carbon\CarbonInterval;

CookiesConsent::register(new CookieGroupDefinition(
    key: 'marketing',
    name: __('cookies-consent::cookies.marketing.name'),
    description: __('cookies-consent::cookies.marketing.description'),
    items: [
        new CookieDefinition(
            name: '_ttp',
            lifetime: CarbonInterval::months(13),
            description: 'Used by TikTok to identify the user.'
        ),
        new CookieDefinition(
            name: 'ttclid',
            lifetime: CarbonInterval::months(13),
            description: 'Used by TikTok to identify the advertisement through which the user arrived on the site.'
        ),
    ],
    onAccepted: function () {
        return <<<'JS'
                ttq.grantConsent();
            JS;
    },
));

Combining Several Tags

Registering a group replaces any group with the same key. If you use several of these tags, register a single marketing group with the cookies of every tag, and grant consent to all of them in its callback:

onAccepted: function () {
    return <<<'JS'
            gtag('consent', 'update', {
                'ad_storage': 'granted',
                'ad_user_data': 'granted',
                'ad_personalization': 'granted',
            });
            fbq('consent', 'grant');
            ttq.grantConsent();
        JS;
},

References

Google Guide: Set up consent mode on websites

Meta Guide: General Data Protection Regulation

TikTok Guide: Pixel cookie consent mode

Testing

composer test
npm test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.