How to Hit 90+ Core Web Vitals on a Framer Site

LCP, INP and CLS meters all in the green
LCP, INP and CLS meters all in the green

A Framer site that scores badly is almost always failing on four things: an uncompressed hero image, fonts that block the first paint, images without declared dimensions, and responsive behavior built in JavaScript state instead of CSS. Fix those four and most sites clear LCP under 2.5s, INP under 200ms and CLS under 0.1 on mobile.

TL;DR

  • The passing bar is LCP under 2.5s, INP under 200ms, CLS under 0.1 — measured at the 75th percentile of real mobile visits, not on your laptop.

  • The hero image is the LCP element on roughly every marketing site I open. Export it at the width it actually renders, times two, and no larger.

  • Layout shift is almost always reserved-space failure: images, embeds and iframes with no declared aspect ratio.

  • The expensive bug nobody looks for: responsive logic implemented in JavaScript state instead of CSS media queries. The phone paints the desktop layout first, then swaps. That is one bad LCP and one CLS event in a single mistake.

  • Test the published URL on a throttled mobile profile. A desktop Lighthouse run will tell you everything is fine.

What the three metrics actually measure

People quote the thresholds and misread what is being timed. That is why the fixes miss.

LCP — under 2.5 seconds

Largest Contentful Paint is the moment the largest image or text block in the initial viewport finishes rendering. It is not “page loaded”. A page can be visually complete at 1.2s and still record an LCP of 4s if the hero image arrives late. LCP is dominated by two things: how many bytes the largest element needs, and how long the browser waits before it even discovers the request.

INP — under 200 milliseconds

Interaction to Next Paint measures the worst-case lag between a tap and the next frame the browser draws, across the whole visit. It replaced First Input Delay, and it is stricter, because it counts processing and rendering, not just the initial delay. INP punishes long tasks on the main thread. On Framer that usually means scroll-linked effects, hover animations on large lists, and third-party scripts.

CLS — under 0.1

Cumulative Layout Shift is the sum of unexpected movement of visible content, scored as impact fraction multiplied by distance fraction. It has nothing to do with speed. A site can load in 900ms and still fail CLS because a font swaps at 800ms and pushes the headline down 4 pixels.

Metric

Passing bar

What it times

Usual cause on a Framer site

LCP

under 2.5s

Render of the largest viewport element

Hero image exported at desktop size, served to a 390px phone

INP

under 200ms

Tap to next painted frame, worst case

Scroll effects and third-party tags holding the main thread

CLS

under 0.1

Unexpected movement of visible content

Images, embeds and fonts with no reserved space

Measure on mobile, or don’t bother

Framer outputs static pages served from a CDN, so your desktop score on a fiber connection will look excellent almost regardless of what you built. That number is meaningless for ranking and meaningless for the visitor on a mid-range Android.

Lab versus field

Lighthouse’s mobile preset simulates a slow 4G connection and applies a 4x CPU slowdown. That is the lab number. The field number — the one Chrome reports through CrUX and the one that feeds Google’s page experience signal — is the 75th percentile of real visits over a rolling 28-day window. Two consequences. First, a lab score of 100 with bad field data is a failed page. Second, after you ship a fix, the field number takes weeks to fully reflect it. Do not panic on day three.

The practical routine

Test the published URL, not the Framer editor preview or a staging domain — the editor runs extra code that will never reach a visitor. Run it twice and read the second run, so the CDN cache is warm. Then open Chrome DevTools, set CPU throttling to 4x and network to Slow 4G, and record a performance trace while you reload. The trace shows you the actual LCP element and the actual long tasks. The score tells you there is a problem; the trace tells you which one.

The hero image is usually the entire score

Size the file to the slot it fills

A full-bleed hero on a 390px-wide phone needs roughly 780 pixels of image to look sharp at 2x density. Uploading a 3,000px export means the phone downloads several times the bytes it can display. The fix is not clever: export at the largest width the element actually renders at, cap the device pixel ratio at 2, and use WebP or AVIF.

Framer generates responsive variants for images placed as normal image layers, which covers most cases. What it cannot help with is an image baked into an SVG, set through a code component’s inline style, or used as a video poster. Those ship at whatever size you gave them. If your LCP element is one of those, that is your bug.

Stop hiding the request

The browser cannot download what it has not discovered. If the hero is loaded by a script, or set as a CSS background inside a stylesheet, the request starts late and LCP inherits the delay. Keep the LCP image as a plain image element in the markup. If you control the markup through custom code, mark it fetchpriority="high" and never loading="lazy" — lazy-loading the hero is one of the most common self-inflicted LCP wounds I see.

Fonts: subset, preload, and accept the trade

Subsetting

Every weight is a separate file. A brand using four static weights plus italics ships six font files before a single word appears. Two fixes. Use a variable font where one file covers the range. And subset to the character ranges you serve — a Latin-only subset drops Cyrillic and Greek glyph data you will never render.

Preloading, honestly

Preloading the one font file used by your headline moves its request to the front of the queue and can pull LCP forward meaningfully on text-led heroes. Add it as custom code in Framer’s site settings head. The objection is fair: preload the wrong file, or preload five, and you have made things worse by competing with the image you actually need. Preload exactly one, verify in the network panel that it is used, and stop.

The swap trade-off

font-display: swap guarantees text is visible immediately in a fallback, then swaps. That protects LCP and destroys CLS if the fallback has different metrics. Match the fallback with size-adjust and ascent-override in your @font-face block so the swap moves nothing, or use font-display: optional and accept that some first-time visitors see the fallback for the whole session.

Layout shift is a reserved-space problem

Every CLS bug is the same bug: something arrives and pushes existing content, because nothing held its place.

  • Images and iframes with no width and height, or no aspect-ratio, collapse to zero and then expand.

  • Third-party embeds — booking widgets, review carousels, chat launchers — insert themselves into the flow after load. Give them a fixed-height container.

  • Cookie banners and promo bars injected at the top of the document push the entire page down. Overlay them instead.

  • Fonts, as above.

Framer will not warn you about any of this. There is no plugin ecosystem for performance or SEO checks, so this is manual work or it does not happen. My website speed optimization work is mostly this list, applied methodically.

The big one: responsive logic in JavaScript instead of CSS

This is the failure that survives every other fix, and it is almost always inside a code component or a third-party template.

Someone needs a different layout on mobile. Instead of using Framer’s breakpoints — which compile to CSS and are resolved before the first paint — they write something like a useState initialized to the desktop branch, then a useEffect that reads window.innerWidth and switches. It looks identical in the editor. On a real phone it does this:

  1. The static HTML paints the desktop layout.

  2. The browser requests whatever assets that branch references, often the large desktop image.

  3. JavaScript hydrates, reads the viewport, and re-renders the mobile layout.

  4. Everything below the swapped block jumps.

You have paid for the wrong image, delayed LCP behind hydration, and generated a layout shift — from one architectural decision.


Framer breakpoints (CSS)

JavaScript state

First paint on a 390px phone

Mobile layout

Desktop layout, then a swap

LCP asset requested

Mobile image

Desktop image, usually the heavier one

Layout shift from the swap

None

One per swapped block

Depends on JS executing

No

Yes

How to spot it in 30 seconds

Open the published page on a phone-sized viewport with JavaScript disabled in DevTools. If the layout is wrong, your responsive logic is in JavaScript. Second check: throttle CPU to 4x, reload, and watch the filmstrip. If you see a desktop frame before the mobile frame, same diagnosis.

The fix

Move the branch into CSS. Framer breakpoints for layout structure, container queries or media queries in custom code where a component genuinely needs them, and useEffect reserved for things that are truly client-only. If a component must know the viewport, render both variants and toggle visibility with CSS so neither one causes a reflow — accepting that you ship slightly more markup in exchange for a correct first paint. This is the single highest-value change in most Framer development audits I run.

Third-party scripts

Each third-party origin costs a DNS lookup, a TLS handshake and a request before a single byte of useful payload arrives. Then the script executes on the main thread, which is what INP measures. A tag manager that loads analytics, a heatmap tool and a chat widget is four scripts, not one.

Three rules. Load nothing in the head that does not have to be there — Framer gives you an end-of-body slot, use it. Load chat widgets and review carousels on interaction, not on page load. And audit quarterly: every marketing tool ever trialled is still in that container.

When chasing 90+ is the wrong approach

I would rather say this plainly than sell you an audit you do not need.

If your field data already passes all three metrics and your lab score is 84, stop. Lighthouse is a diagnostic, not a target. Optimizing a synthetic number that real users never experience is theater.

If the only way to hit 90 is deleting the animation that makes the product feel considered, weigh it properly. A 3-point score gain is not worth a worse site.

And if performance is bad because Framer is being forced to do a job it cannot do, the fix is not optimization. Framer has no server-side logic, no native membership or gating, and no WooCommerce. Commerce runs through third-party embeds like Shopify buy buttons, and you cannot remove the JavaScript those embeds require. If your site is a 400-SKU store or a gated member portal, a fast Framer build is not available at any price, and I will tell you that on the first call rather than after the invoice. For content sites, landing pages and marketing sites, 90+ on mobile is a normal outcome, not a stretch goal.

For sites where the problem is structural rather than technical, website SEO work and a rebuild usually cost less than repeatedly patching. A fixed-price speed audit is $900; full builds start at a flat $1,200 base, and the full breakdown is on pricing.

The order I work in

  1. Confirm the LCP element with a throttled trace. Never guess.

  2. Fix that one asset: size, format, discovery, priority.

  3. Disable JavaScript and check the mobile layout. Fix any JS-driven responsive branch.

  4. Declare dimensions on every image, iframe and embed.

  5. Subset fonts, preload one file, fix the fallback metrics.

  6. Cut or defer third-party scripts.

  7. Re-measure in the lab, then wait 28 days for field data.

Roughly 80% of the gain lands in steps 2 and 3.

FAQ

Why is my Framer site fast on desktop but slow on mobile?

Because mobile testing applies a 4x CPU slowdown and a slow 4G network, while your desktop has neither. The two usual causes are a hero image sized for desktop being downloaded by a phone, and responsive logic written in JavaScript state, which makes the phone paint the desktop layout first and swap after hydration.

Can a Framer site actually score 90+ on Core Web Vitals?

Yes, for marketing sites, landing pages and blogs. Framer outputs static pages from a CDN, so the network side starts strong. Scores fail because of what gets added on top: oversized images, unsubsetted fonts, undeclared image dimensions and third-party scripts. Fix those and 90+ on mobile is a normal result, not an exception.

What is the single fastest fix for a bad LCP score?

Resize the hero image. Export it at the width it actually renders times two, convert to WebP or AVIF, and make sure it is a plain image element that is not lazy-loaded. On most sites the largest contentful element is the hero, so cutting its byte weight moves LCP more than every other change combined.

How long until my Core Web Vitals score improves after fixing things?

Lab scores update immediately. Field data does not. Chrome reports Core Web Vitals as the 75th percentile of real visits over a rolling 28-day window, so a fix shipped today only reaches full effect about four weeks later. Judge your work by the lab trace and the trend, not by the field number on day three.

If you want a second opinion on yours, send me your URL and I will come back within 48 hours with a crawl summary, a redirect risk list and a fixed quote.



© 2026 Framerfry. Built by an Official Framer Expert.

framerfry