Pular para o conteúdo
VTEXPerformanceCore Web Vitals

VTEX Performance: how to achieve Lighthouse 90+ in practice

10 min

Achieving Lighthouse 90+ on a VTEX store is possible, but requires intentional technical work. The platform, especially on Store Framework, tends to generate heavy bundles due to the app system nature. Each installed app adds JavaScript to the bundle, and stores with many apps easily exceed 1MB of compressed JS. This guide presents practical techniques tested on real stores to raise Lighthouse scores without sacrificing functionality.

Installed app audit

The first step is auditing all installed apps in the store. Run vtex list on the production workspace to see the complete list. For each app, ask: is this app still used? Is there a lighter alternative? Does the benefit justify the bundle impact? Third-party apps are the biggest culprits. Reviews that load entire frameworks, popups with jQuery, personalization tools that add 300KB+ of JS. Remove non-essential apps and replace with lighter alternatives when possible. In a real case, removing 5 unused apps reduced the bundle by 400KB and improved LCP from 4.2s to 2.1s.

Image optimization

Images account for most of an e-commerce page's weight. On VTEX, use the native resizing service: add width and height parameters to the image URL (?width=800&height=600). Configure lazy loading on all images below the fold. The hero image (main banner) should have preload via link rel=preload tag in the head. Set explicit width and height on all img elements to prevent CLS. Use modern formats: VTEX's CDN supports automatic WebP conversion via URL parameter. For banners, consider using picture with srcset to serve different sizes per breakpoint.

Fonts and CSS

Custom fonts without proper configuration cause flash of invisible text (FOIT) or flash of unstyled text (FOUT), impacting LCP and CLS. Configure font-display: swap on all @font-face declarations. Preload critical fonts with link rel=preload as=font crossorigin. Limit the number of loaded weights and styles: each variation adds 20-50KB. For CSS, avoid inline styles in custom blocks. Use CSS Handles efficiently, without deeply nested selectors. Remove CSS from uninstalled apps that may remain in the bundle due to cache. Use Chrome DevTools Coverage tab to identify unused CSS.

JavaScript and hydration

The biggest performance challenge in Store Framework is JavaScript. Full React hydration on the client means all server-rendered HTML needs to be reprocessed in the browser. To mitigate: use React.lazy with Suspense for heavy components below the fold. Avoid useEffect with fetch on component mount. Prefer server-provided data via GraphQL with cache. Set defer on third-party scripts (analytics, chat, pixels). Delay loading of interactive components until user interaction (lazy hydration pattern). For GTM scripts, use the post-window-load loading strategy to avoid blocking the main thread during initial render.

Cache and CDN

VTEX has an edge cache layer that needs proper configuration. For category and search pages, configure page cache with appropriate duration (5-15 minutes is a good default). For GraphQL APIs, use the @cacheControl directive with maxAge and scope PUBLIC for non-user-dependent data. Static asset cache headers should have long max-age (1 year) with filename fingerprinting for invalidation. Monitor cache hits vs misses in the VTEX panel. A hit ratio below 80% indicates configuration problems. For stores with segment personalization, use ESI (Edge Side Includes) or cookie-based cache when available.

Continuous monitoring

Performance is not a project with a start and end. It is an ongoing process. Set up Core Web Vitals alerts in Search Console. Run Lighthouse CI on each deploy (configure in GitHub Actions or your CI pipeline). Monitor Real User Metrics (RUM) with tools like web-vitals library integrated with GA4. Compare field metrics (CrUX) with lab metrics (Lighthouse) monthly. When installing a new app, measure impact before and after with SpeedCurve or WebPageTest. Define a performance budget: maximum bundle size, maximum acceptable LCP, zero CLS. If a new feature exceeds the budget, optimize before going to production.

The headless path: when optimization is not enough

If the store has dozens of apps, the bundle exceeds 1.5MB, and each point optimization yields diminishing returns, it is time to consider the headless path. Two options: migrate to FastStore (Next.js frontend keeping VTEX backend) or adopt deco.cx as an independent frontend layer. In both cases, the result is consistent Lighthouse 90+ without needing to optimize app by app. The VTEX backend remains intact: checkout, catalog, pricing and orders work normally. Only the presentation layer changes. The investment is larger than point optimization ($6,000-$16,000 to rebuild the frontend), but the result is sustainable long-term. For new VTEX projects in 2025, starting with FastStore from the beginning avoids accumulating performance technical debt.