1
At a glance the headline numbers for CMS impact
Performance impact by cms provider.
8
Providers ranked
After min-sites filter
80,309
Sites in sample
Combined across all items
0.00
Best CLS (p75)
Wix
The State of Web Vitals · Q2 2026 · 189,915 sites · all devices field datacorewebvitals.io/state-of-cwv
2
The ranking sorted by CLS at p75, fastest first
| # | Provider | CLS p75 | Passing | Sites | |
|---|---|---|---|---|---|
| 1 | Wix | 0.00 | 97.7% | 5,217 | |
| 2 | Joomla | 0.01 | 91.6% | 2,325 | |
| 3 | Typo3 | 0.02 | 92.2% | 95 | |
| 4 | Shopify | 0.02 | 94.0% | 7,625 | |
| 5 | WordPress | 0.03 | 89.0% | 59,040 | |
| 6 | Squarespace | 0.04 | 89.7% | 2,028 | |
| 7 | Drupal | 0.04 | 87.9% | 2,990 | |
| 8 | Adobe commerce | 0.11 | 73.3% | 989 |
The State of Web Vitals · Q2 2026 · 189,915 sites · all devices field datacorewebvitals.io/state-of-cwv
3
Passing CLS per provider which group passes the CLS most often
ProviderSitesPassing CLSp75
Wix 2.7%
5,217
0.00
Shopify 4%
7,625
0.02
Webflow 0%
15
0.03
Typo3 0%
95
0.02
Ghost 0%
37
0.06
Joomla 1.2%
2,325
0.01
Squarespace 1.1%
2,028
0.04
Wordpress 31.1%
59,040
0.03
Drupal 1.6%
2,990
0.04
Hubspot cms 0%
5
0.03
Adobe commerce 0.5%
989
0.11
Storyblok 0%
11
0.31
Good
Needs Improvement
Poor
Sorted best-passing first · median colored by its own rating · pass = good CLS (0.1 at p75) · one value per site
98% of Wix sites pass CLS. Adobe commerce trails 25 points behind, leaving 27% of its sites failing. computed
The State of Web Vitals · Q2 2026 · 189,915 sites · all devices field datacorewebvitals.io/state-of-cwv
4
All five vitals at once the whole category without toggling - cell is the pass rate, small number the p75
LCP
INP
CLS
FCP
TTFB
Wix 2.7%
951.6s
9788ms
980.00
931.1s
81666ms
Shopify 4%
961.7s
96123ms
940.02
951.1s
96516ms
Storyblok 0%
642.8s
78146ms
640.31
642.0s
301.4s
Typo3 0%
941.5s
87101ms
920.02
891.3s
71878ms
Webflow 0%
1001.7s
10084ms
930.03
931.2s
71839ms
Wordpress 31.1%
702.7s
98104ms
890.03
592.3s
311.8s
Drupal 1.6%
921.7s
97102ms
880.04
861.5s
68929ms
Joomla 1.2%
902.0s
9596ms
920.01
811.7s
551.1s
Adobe commerce 0.5%
782.4s
98118ms
730.11
682.0s
311.6s
Squarespace 1.1%
892.1s
10092ms
900.04
861.5s
96541ms
Ghost 0%
1001.4s
10087ms
920.06
1001.1s
89581ms
Hubspot cms 0%
1001.5s
100101ms
800.03
1001.3s
80654ms
60%95%+ passing
Cell: pass rate, small number = p75 · faded rows: under 100 sites
One row per provider, one column per vital - the cell is the share of sites passing, the small number the p75. No toggling needed to see where the category actually differs.
Wix leads on CLS: 98% of its sites pass. Adobe commerce trails at 73%. computed
The State of Web Vitals · Q2 2026 · 189,915 sites · all devices field datacorewebvitals.io/state-of-cwv
Related signals
Video (7) →
A/B testing (12) →
JS libraries (25) →
Customer data platforms (6) →
Chrome field data from 189,915 sites, representing millions of real page loads · How we measured
Chrome field data from 189,915 sites, representing millions of real page loads. How we measured.
Live queries (2) — admin only
Query #1:
415.6 ms
SELECT s.entities.cms 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 sites s
WHERE s.entities.cms IS NOT NULL AND CAST(s.entities.cms AS VARCHAR) <> ''
GROUP BY s.entities.cms;
Query #2:
44.1 ms
SELECT COUNT(*) AS n FROM sites;
JSON file lookups (5)
20260625/meta.json
0.03 ms
20260625/types.json
0.49 ms
20260625/loaf-scripts.json
0.39 ms
20260625/menu.json
0.12 ms
20260625/providers.json
5.21 ms