Core Web Vitals and performance optimisation for headless Wix
Module 43: Wix Headless SEO | Lesson 498 of 688 | 50 min read
By Michael Andrews, Wix SEO Expert UK
One of the most compelling reasons to go headless with Wix is the ability to achieve perfect Core Web Vitals scores — metrics that directly influence your Google rankings and that standard Wix sites frequently struggle with due to platform-imposed scripts and editor overhead. Without the Wix editor runtime, Corvid scripts, and third-party app widgets loading on every page, you control every byte that enters the browser. This lesson provides four comprehensive, code-specific guides for optimising LCP, CLS, and INP on your headless Wix frontend, plus a step-by-step measurement workflow that shows you exactly how to identify and verify improvements using the right tools.
LCP Optimisation: Achieving Sub-2.5 Second Loading
Largest Contentful Paint measures how long it takes for the largest visible element in the viewport to render. On most headless Wix sites this will be the hero image, a large heading, or a featured image on a blog post. The LCP threshold for "good" is 2.5 seconds, and the most common causes of poor LCP are unoptimised hero images, render-blocking stylesheets, slow server response times, and missing resource hints. All of these are fully within your control on a headless frontend.
Wix Media CDN supports URL-based image transformations that let you serve exactly the right image size for each device without storing multiple image variants. Append parameters like w=1200&q=80&f=webp to your Wix Media URL to get a 1200px wide, 80% quality WebP image. For your hero image specifically, you must also signal to the browser that this image is high-priority so it starts downloading before the browser has finished parsing the full HTML.
Optimising LCP with Next.js image preloading and Wix Media CDN
- Identify your LCP element on each key page template using Chrome DevTools: open DevTools, run a Lighthouse report, and check the "LCP element" detail in the Opportunities section
- For hero images served from Wix Media CDN, construct the optimised URL by appending transformation parameters: https://static.wixstatic.com/media/[imageId]/v1/fill/w_1200,h_630,q_80,enc_avif/[filename]
- In your Next.js page component, use the Next.js Image component with priority={true} for your hero image — this automatically adds fetchpriority="high" and a preload link tag to the document head
- Set explicit width and height props on the Next.js Image component matching your intended display dimensions — this prevents layout shift and allows the browser to calculate aspect ratio before the image loads
- Add a <link rel="preconnect" href="https://static.wixstatic.com"> tag in your document head to establish the connection to Wix Media CDN before the browser parses your image tags
- For text-based LCP (large headings), ensure your web font is preloaded: add <link rel="preload" as="font" href="/fonts/your-font.woff2" crossOrigin="anonymous"> in the head
- Set font-display: swap or font-display: optional in your @font-face declarations so text renders immediately in a system font while your custom font loads, preventing invisible text from blocking LCP
- Verify server response time (TTFB) is under 800ms by deploying to a Vercel edge network or Cloudflare Pages — TTFB over 800ms will prevent you from hitting a good LCP score regardless of image optimisation
- After deployment, run PageSpeed Insights on your production URL and verify the LCP is under 2.5 seconds — if not, check the Diagnostics section for the specific blocking resource causing the delay
- Set up real user monitoring using the web-vitals npm package to capture field LCP data from actual users: import { onLCP } from "web-vitals" and send results to your analytics platform

CLS Prevention: Achieving Zero Layout Shift
Cumulative Layout Shift measures the total amount of unexpected visual movement that occurs during page load. A score above 0.1 is considered "needs improvement" and above 0.25 is "poor." The most common causes on headless Wix sites are images without explicit dimensions, late-loading banner ads or cookie consent banners, dynamically injected content that pushes existing content down, and web fonts that cause text reflow when they swap in. All of these are preventable with the right implementation patterns.
The Wix Media API returns the original dimensions of every image stored in the Wix Media Manager. You must use these dimensions to set the width and height attributes on your image elements or to configure the CSS aspect-ratio property on image containers. Without explicit dimensions, the browser cannot reserve space for the image before it loads, resulting in a layout shift when the image arrives and pushes surrounding content downward.
Preventing CLS with explicit image dimensions from the Wix Media API
- When fetching image data from the Wix API, always request the image width and height fields alongside the URL — the Wix Blog API returns these in the coverImage.width and coverImage.height fields
- In your Next.js Image component, always set the width and height props to the original image dimensions from the Wix API: <Image src={imageUrl} width={post.coverImage.width} height={post.coverImage.height} alt={post.coverImage.altText} />
- For responsive images that scale with their container, use the fill prop combined with a parent container that has position: relative and an explicit aspect ratio set via CSS: aspect-ratio: 16 / 9
- For inline images in blog post rich text content, extract the width and height from the Wix rich text image node data and set them as HTML attributes on the rendered img element
- For any images where dimensions are unavailable, set a default aspect ratio container using padding-bottom technique: padding-bottom: 56.25% (for 16:9) on the wrapper element
- Prevent cookie consent banner CLS by using position: fixed for the banner so it overlays content rather than pushing it down, and set the initial page margin-bottom to 0
- Reserve space for any dynamically loaded content (ads, embedded widgets) by setting min-height on their containers before the content loads
- Use font-display: optional for decorative fonts if possible — this prevents font swap entirely by only using the custom font if it loads before the first render, eliminating text reflow CLS
- After implementing these changes, measure CLS using Chrome DevTools: open the Performance panel, click Record, load your page, then click Stop and look for the Layout Shift markers in the timeline
- Verify your CLS score is below 0.1 in PageSpeed Insights field data — remember that lab data and field data can differ, so use the Chrome User Experience Report data when available
INP Optimisation: Responsive Interactions with Code Splitting
Interaction to Next Paint replaced First Input Delay as a Core Web Vital in March 2024. INP measures the longest interaction delay across the entire page session, not just the first interaction. A score under 200ms is "good," 200-500ms "needs improvement," and above 500ms is "poor." On JavaScript-heavy pages, the main thread gets blocked by long tasks — parsing large JavaScript bundles, executing complex React renders, or running synchronous third-party scripts. Code splitting, React Server Components, and deferred script loading are your primary tools for keeping the main thread responsive.
Reducing INP with code splitting and deferred JavaScript loading
- Measure your current INP using the web-vitals package: import { onINP } from "web-vitals" and log the value with its attribution data to identify which interaction is slowest
- Analyze your JavaScript bundle size using Next.js Bundle Analyzer: add ANALYZE=true next build to your build command and inspect which packages contribute most to your bundle
- Convert all page components that do not require interactivity to React Server Components by removing "use client" from the file — Server Components ship zero JavaScript to the browser
- For interactive components like search bars, filter menus, and carousels, use next/dynamic with ssr: false and loading: () => <Skeleton /> to load them after the initial page render
- Split heavy third-party libraries like date formatters, markdown parsers, or chart libraries using dynamic imports inside event handlers rather than importing them at the module level
- Move all Wix API calls to Server Components or getStaticProps/generateStaticParams so they execute at build time or on the server, not in the browser where they would block the main thread
- Defer non-critical third-party scripts (analytics, chat widgets, heatmap tools) using the Next.js Script component with strategy="lazyOnload" so they only load after the page is fully interactive
- Use React.startTransition() to wrap state updates that trigger expensive re-renders — this marks them as non-urgent and prevents them from blocking user input responses
- Break long JavaScript tasks (>50ms) into smaller chunks using scheduler.yield() or queueMicrotask() to give the browser opportunities to handle user input between chunks
- After optimising, re-measure INP in production using Chrome UX Report data in PageSpeed Insights — field INP data from real users is more reliable than lab measurements for this metric
Measuring Core Web Vitals with the Right Tools
There are two types of Core Web Vitals data: lab data collected by automated tools in a controlled environment, and field data collected from real users visiting your site. Lab data is useful for development and debugging because you can run it on demand. Field data is what Google actually uses in its ranking algorithm via the Chrome User Experience Report, so both matter but for different reasons. A page can have excellent lab data and poor field data if real-world network conditions, device capabilities, or third-party scripts behave differently than the lab simulation.
Setting up a complete Core Web Vitals measurement workflow
- Install the web-vitals npm package in your Next.js project: npm install web-vitals — this gives you access to all three Core Web Vitals metrics from real users
- Create a lib/analytics.ts file with a reportWebVitals function that imports onLCP, onCLS, and onINP from web-vitals and sends each metric to your analytics endpoint
- Call your reportWebVitals function from the Next.js app/layout.tsx using a useEffect hook (in a Client Component wrapper) to register the metric listeners after the page mounts
- In PageSpeed Insights (pagespeed.web.dev), enter your production URL and review both the Field Data section (real Chrome users) and the Lab Data section (Lighthouse simulation)
- In Chrome DevTools, run Lighthouse in Performance mode to get lab measurements during development — use Throttled 4G CPU slowdown to simulate mobile conditions
- Open the Performance panel in Chrome DevTools, click Record, interact with the page (click buttons, scroll, type in inputs), then stop and review the INP attribution in the Main thread track
- In Google Search Console, navigate to Core Web Vitals under Experience — this shows aggregated field data from Chrome users visiting your site over the past 28 days
- Use the Chrome UX Report (CrUX) API to query field data programmatically for any URL: send a POST request to the CrUX API endpoint with your URL and metrics array
- Set up a weekly Lighthouse CI run in your GitHub Actions workflow using the lighthouse-ci npm package to catch performance regressions before they reach production
- Create a performance budget in your Next.js config or Lighthouse CI config that fails builds if LCP exceeds 2.5s, CLS exceeds 0.1, or JavaScript bundle size exceeds your target threshold
Edge Caching and CDN Strategy for Headless Wix
Deploying your headless Wix frontend to an edge network like Vercel or Cloudflare Pages places your pre-rendered HTML on servers closest to your users globally, dramatically reducing TTFB and LCP for international visitors. Combined with Incremental Static Regeneration, you get static-site speed with the content freshness of server-side rendering — the best of both worlds for a content-heavy Wix-powered site.
- Set Cache-Control: public, s-maxage=31536000, immutable on all static assets (JS, CSS, fonts, images with content-hashed filenames) so edge nodes cache them indefinitely
- Set Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400 on ISR-enabled pages so edge nodes serve cached HTML for up to an hour while regenerating in the background
- Configure Wix Media CDN image URLs with explicit format, width, and quality parameters so the CDN serves optimised variants rather than original high-resolution uploads
- Implement a service worker using Workbox to cache static assets and previously visited pages for repeat visitors, enabling instant navigation even on slow connections
- Use Vercel's Edge Middleware or Cloudflare Workers to implement A/B testing and personalisation at the edge without adding JavaScript to the client bundle
- Configure Brotli compression on all text responses — Brotli typically achieves 15-20% better compression than gzip for HTML, CSS, and JavaScript files
- Lazy load below-the-fold images using the Next.js Image component's default loading="lazy" behaviour — only apply priority={true} to the first above-the-fold image
- Split your sitemap into per-section sitemaps (blog.xml, products.xml, pages.xml) and serve them from edge functions so they are always fresh without being regenerated on every request
This lesson on Core Web Vitals and performance optimisation for headless Wix is part of Module 43: Wix Headless SEO in The Most Comprehensive Complete Wix SEO Course in the World (2026 Edition). Created by Michael Andrews, the UK's No.1 Wix SEO Expert with 14 years of hands-on experience, 750+ completed Wix SEO projects and 425+ verified five-star reviews.