Consent platforms impact

Consent management platforms and their CWV impact.

Field data PhoneDesktopAll Scope All sites Q2 2026 edition · Phone field outcomes
Metric LCP INP CLS
1

At a glance the headline numbers for Consent platforms impact

Consent management platforms and their CWV impact.

16
Providers ranked
After min-sites filter
53,349
Sites in sample
Combined across all items
2.1s
Best LCP (p75)
Civic Cookie Control
The State of Web Vitals · Q2 2026 · 189,915 sites · phone field datacorewebvitals.io/state-of-cwv
2

The ranking sorted by LCP at p75, fastest first

# Provider LCP p75 Passing Sites
1 Civic Cookie Control 2.1s 83.3%
8,748
2 Iubenda 2.2s 81.1%
848
3 CookieFirst 2.3s 81.2%
742
4 Cookie Information 2.3s 81.0%
160
5 Cookie Script 2.3s 81.4%
1,305
6 Cookiebot 2.3s 79.6%
8,148
7 Didomi 2.3s 79.6%
1,148
8 Termly 2.4s 79.6%
339
9 Quantcast Choice 2.4s 76.7%
9,653
10 OneTrust 2.6s 73.7%
1,878
11 Osano 2.7s 67.6%
95
12 Borlabs Cookie 2.7s 69.7%
5,162
13 CookieYes 2.7s 69.0%
4,982
14 TrustArc 2.7s 70.0%
82
15 Complianz 2.9s 64.4%
8,942
16 GDPR Cookie Compliance 3.0s 64.9%
1,117
The State of Web Vitals · Q2 2026 · 189,915 sites · phone field datacorewebvitals.io/state-of-cwv
Related signals Push notifications (5) → CMS (17) → Analytics (20) → Search (6) → Chrome field data from 189,915 sites, representing millions of real page loads · How we measured
Live queries (3) — admin only
Query #1: 37.6 ms
SELECT 1 AS ok FROM site_providers LIMIT 0;
Query #2: 671.3 ms
SELECT CAST(p.key AS VARCHAR) AS e, COUNT(*) AS sites,
  COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL) AS all_lcp_n,
  quantile_disc(s.crux."all".lcp, 0.5) AS all_lcp_median,
  quantile_disc(s.crux."all".lcp, 0.75) AS all_lcp_p75,
  COUNT(*) FILTER (WHERE s.crux."all".lcp <= 2500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL), 0) AS all_lcp_good,
  COUNT(*) FILTER (WHERE s.crux."all".lcp > 2500 AND s.crux."all".lcp <= 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL), 0) AS all_lcp_ni,
  COUNT(*) FILTER (WHERE s.crux."all".lcp > 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".lcp IS NOT NULL), 0) AS all_lcp_poor,
  COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL) AS all_inp_n,
  quantile_disc(s.crux."all".inp, 0.5) AS all_inp_median,
  quantile_disc(s.crux."all".inp, 0.75) AS all_inp_p75,
  COUNT(*) FILTER (WHERE s.crux."all".inp <= 200) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL), 0) AS all_inp_good,
  COUNT(*) FILTER (WHERE s.crux."all".inp > 200 AND s.crux."all".inp <= 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL), 0) AS all_inp_ni,
  COUNT(*) FILTER (WHERE s.crux."all".inp > 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".inp IS NOT NULL), 0) AS all_inp_poor,
  COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL) AS all_cls_n,
  quantile_disc(s.crux."all".cls, 0.5) AS all_cls_median,
  quantile_disc(s.crux."all".cls, 0.75) AS all_cls_p75,
  COUNT(*) FILTER (WHERE s.crux."all".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL), 0) AS all_cls_good,
  COUNT(*) FILTER (WHERE s.crux."all".cls > 0.1 AND s.crux."all".cls <= 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL), 0) AS all_cls_ni,
  COUNT(*) FILTER (WHERE s.crux."all".cls > 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".cls IS NOT NULL), 0) AS all_cls_poor,
  COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL) AS all_fcp_n,
  quantile_disc(s.crux."all".fcp, 0.5) AS all_fcp_median,
  quantile_disc(s.crux."all".fcp, 0.75) AS all_fcp_p75,
  COUNT(*) FILTER (WHERE s.crux."all".fcp <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL), 0) AS all_fcp_good,
  COUNT(*) FILTER (WHERE s.crux."all".fcp > 1800 AND s.crux."all".fcp <= 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL), 0) AS all_fcp_ni,
  COUNT(*) FILTER (WHERE s.crux."all".fcp > 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".fcp IS NOT NULL), 0) AS all_fcp_poor,
  COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL) AS all_ttfb_n,
  quantile_disc(s.crux."all".ttfb, 0.5) AS all_ttfb_median,
  quantile_disc(s.crux."all".ttfb, 0.75) AS all_ttfb_p75,
  COUNT(*) FILTER (WHERE s.crux."all".ttfb <= 800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL), 0) AS all_ttfb_good,
  COUNT(*) FILTER (WHERE s.crux."all".ttfb > 800 AND s.crux."all".ttfb <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL), 0) AS all_ttfb_ni,
  COUNT(*) FILTER (WHERE s.crux."all".ttfb > 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."all".ttfb IS NOT NULL), 0) AS all_ttfb_poor,
  COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL) AS phone_lcp_n,
  quantile_disc(s.crux."phone".lcp, 0.5) AS phone_lcp_median,
  quantile_disc(s.crux."phone".lcp, 0.75) AS phone_lcp_p75,
  COUNT(*) FILTER (WHERE s.crux."phone".lcp <= 2500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL), 0) AS phone_lcp_good,
  COUNT(*) FILTER (WHERE s.crux."phone".lcp > 2500 AND s.crux."phone".lcp <= 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL), 0) AS phone_lcp_ni,
  COUNT(*) FILTER (WHERE s.crux."phone".lcp > 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".lcp IS NOT NULL), 0) AS phone_lcp_poor,
  COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL) AS phone_inp_n,
  quantile_disc(s.crux."phone".inp, 0.5) AS phone_inp_median,
  quantile_disc(s.crux."phone".inp, 0.75) AS phone_inp_p75,
  COUNT(*) FILTER (WHERE s.crux."phone".inp <= 200) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL), 0) AS phone_inp_good,
  COUNT(*) FILTER (WHERE s.crux."phone".inp > 200 AND s.crux."phone".inp <= 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL), 0) AS phone_inp_ni,
  COUNT(*) FILTER (WHERE s.crux."phone".inp > 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".inp IS NOT NULL), 0) AS phone_inp_poor,
  COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL) AS phone_cls_n,
  quantile_disc(s.crux."phone".cls, 0.5) AS phone_cls_median,
  quantile_disc(s.crux."phone".cls, 0.75) AS phone_cls_p75,
  COUNT(*) FILTER (WHERE s.crux."phone".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL), 0) AS phone_cls_good,
  COUNT(*) FILTER (WHERE s.crux."phone".cls > 0.1 AND s.crux."phone".cls <= 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL), 0) AS phone_cls_ni,
  COUNT(*) FILTER (WHERE s.crux."phone".cls > 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".cls IS NOT NULL), 0) AS phone_cls_poor,
  COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL) AS phone_fcp_n,
  quantile_disc(s.crux."phone".fcp, 0.5) AS phone_fcp_median,
  quantile_disc(s.crux."phone".fcp, 0.75) AS phone_fcp_p75,
  COUNT(*) FILTER (WHERE s.crux."phone".fcp <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL), 0) AS phone_fcp_good,
  COUNT(*) FILTER (WHERE s.crux."phone".fcp > 1800 AND s.crux."phone".fcp <= 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL), 0) AS phone_fcp_ni,
  COUNT(*) FILTER (WHERE s.crux."phone".fcp > 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".fcp IS NOT NULL), 0) AS phone_fcp_poor,
  COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL) AS phone_ttfb_n,
  quantile_disc(s.crux."phone".ttfb, 0.5) AS phone_ttfb_median,
  quantile_disc(s.crux."phone".ttfb, 0.75) AS phone_ttfb_p75,
  COUNT(*) FILTER (WHERE s.crux."phone".ttfb <= 800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL), 0) AS phone_ttfb_good,
  COUNT(*) FILTER (WHERE s.crux."phone".ttfb > 800 AND s.crux."phone".ttfb <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL), 0) AS phone_ttfb_ni,
  COUNT(*) FILTER (WHERE s.crux."phone".ttfb > 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."phone".ttfb IS NOT NULL), 0) AS phone_ttfb_poor,
  COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL) AS desktop_lcp_n,
  quantile_disc(s.crux."desktop".lcp, 0.5) AS desktop_lcp_median,
  quantile_disc(s.crux."desktop".lcp, 0.75) AS desktop_lcp_p75,
  COUNT(*) FILTER (WHERE s.crux."desktop".lcp <= 2500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL), 0) AS desktop_lcp_good,
  COUNT(*) FILTER (WHERE s.crux."desktop".lcp > 2500 AND s.crux."desktop".lcp <= 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL), 0) AS desktop_lcp_ni,
  COUNT(*) FILTER (WHERE s.crux."desktop".lcp > 4000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".lcp IS NOT NULL), 0) AS desktop_lcp_poor,
  COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL) AS desktop_inp_n,
  quantile_disc(s.crux."desktop".inp, 0.5) AS desktop_inp_median,
  quantile_disc(s.crux."desktop".inp, 0.75) AS desktop_inp_p75,
  COUNT(*) FILTER (WHERE s.crux."desktop".inp <= 200) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL), 0) AS desktop_inp_good,
  COUNT(*) FILTER (WHERE s.crux."desktop".inp > 200 AND s.crux."desktop".inp <= 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL), 0) AS desktop_inp_ni,
  COUNT(*) FILTER (WHERE s.crux."desktop".inp > 500) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".inp IS NOT NULL), 0) AS desktop_inp_poor,
  COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL) AS desktop_cls_n,
  quantile_disc(s.crux."desktop".cls, 0.5) AS desktop_cls_median,
  quantile_disc(s.crux."desktop".cls, 0.75) AS desktop_cls_p75,
  COUNT(*) FILTER (WHERE s.crux."desktop".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL), 0) AS desktop_cls_good,
  COUNT(*) FILTER (WHERE s.crux."desktop".cls > 0.1 AND s.crux."desktop".cls <= 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL), 0) AS desktop_cls_ni,
  COUNT(*) FILTER (WHERE s.crux."desktop".cls > 0.25) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".cls IS NOT NULL), 0) AS desktop_cls_poor,
  COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL) AS desktop_fcp_n,
  quantile_disc(s.crux."desktop".fcp, 0.5) AS desktop_fcp_median,
  quantile_disc(s.crux."desktop".fcp, 0.75) AS desktop_fcp_p75,
  COUNT(*) FILTER (WHERE s.crux."desktop".fcp <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL), 0) AS desktop_fcp_good,
  COUNT(*) FILTER (WHERE s.crux."desktop".fcp > 1800 AND s.crux."desktop".fcp <= 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL), 0) AS desktop_fcp_ni,
  COUNT(*) FILTER (WHERE s.crux."desktop".fcp > 3000) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".fcp IS NOT NULL), 0) AS desktop_fcp_poor,
  COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL) AS desktop_ttfb_n,
  quantile_disc(s.crux."desktop".ttfb, 0.5) AS desktop_ttfb_median,
  quantile_disc(s.crux."desktop".ttfb, 0.75) AS desktop_ttfb_p75,
  COUNT(*) FILTER (WHERE s.crux."desktop".ttfb <= 800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL), 0) AS desktop_ttfb_good,
  COUNT(*) FILTER (WHERE s.crux."desktop".ttfb > 800 AND s.crux."desktop".ttfb <= 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL), 0) AS desktop_ttfb_ni,
  COUNT(*) FILTER (WHERE s.crux."desktop".ttfb > 1800) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE s.crux."desktop".ttfb IS NOT NULL), 0) AS desktop_ttfb_poor
                    FROM site_providers p
                    JOIN sites s ON s.origin = p.origin
                    WHERE p.category = 'consent'
                      AND p.key IS NOT NULL AND CAST(p.key AS VARCHAR) <> ''
                    GROUP BY p.key;
Query #3: 41.7 ms
SELECT COUNT(*) AS n FROM sites;
JSON file lookups (5)
20260625/meta.json 0.05 ms
20260625/types.json 0.62 ms
20260625/loaf-scripts.json 0.56 ms
20260625/menu.json 0.19 ms
20260625/providers.json 4.56 ms