Skip to content
Back to playbooks
Engineering

Landing pages that load in under a second

The budget, the stack, and the checklist we use to ship marketing pages that paint in under a second on a mid-range phone.

Entatan engineering · 6 min read

Landing pages exist to convert paid traffic. Paid traffic arrives on phones, on cellular, mid-scroll. A page that takes three seconds to show anything is paying for clicks it never gets to use, and the ad platform will never tell you that is why the numbers are soft.

We hold marketing pages to a budget: first paint under a second and interactive under two, on a mid-range phone over a throttled connection. This playbook is how. Most of it is about not adding things, which turns out to be the hard part.

Why a second

A second is not a magic number. It is the point past which the cost of every additional tool, font, and script becomes visible in the conversion rate. Below it, teams stop arguing about performance because there is nothing to argue about. That is the real benefit: a budget that removes a class of meeting.

The budget

MetricTargetMeasured on
First contentful paintUnder 1.0 sMid-range Android, throttled 4G
Largest contentful paintUnder 1.5 sSame profile
Time to interactiveUnder 2.0 sSame profile
JavaScript, compressedUnder 90 KBProduction build
Page weight, first viewUnder 400 KBAbove the fold
Cumulative layout shiftUnder 0.05Field data
Third-party requests3 or fewerBefore consent

The targets are fixed per page type, checked in CI, and reviewed against field data weekly. A page can be over budget for a day with an issue open against it. It cannot be over budget for a week.

The stack

Static HTML from a framework build, served from a CDN edge with long cache headers and an instant purge on deploy. Components rendered on the server. Client JavaScript only for the pieces that need it: the form, the consent banner, the one animation that earns its place. Everything else is HTML and CSS.

We use the same framework for landing pages that we use for products, but with a separate build target and a separate budget check. A landing page that imports the product's component library inherits the product's bundle. Keep them apart, and share tokens instead of components.

What we cut

  • Client-side routing. A landing page has one route.
  • Animation libraries. CSS handles what a landing page needs.
  • Icon fonts. Inline the six icons you actually use.
  • Video autoplay above the fold. A poster image and a click.
  • Chat widgets before interaction. Load on scroll or on intent, never on paint.
  • Testing scripts that block render. Test at the edge or on the server.

Fonts, images, scripts

Two font files, subset to the characters on the page, preloaded, with a fallback that has matching metrics so nothing jumps when they arrive. Images sized to their slot, in a modern format, with explicit dimensions and lazy loading below the fold. The hero image is the one exception: preload it, and make it small.

Scripts are the whole problem. Every script loads after paint, after consent where consent applies, and only on the page that uses it. We keep a manifest of every third-party script on every page with the name of the person who asked for it. Scripts without an owner get removed at the weekly review, and nobody has ever asked for one back.

The tag manager problem

Tag managers are how a fast page becomes a slow one without a deploy. Anyone with access can add a script that runs on every page, and nobody sees the cost until the weekly report. We do not ban tag managers. We put them behind the same budget check as code: any container change that pushes a page over budget fails the check and gets reverted.

The better answer is server-side tagging. Events go from your server to the platforms. The browser loads one small script or none. This is the same pipe the performance-marketing playbook describes, and it makes the page faster and the data better at the same time.

The checklist

  1. Static build, edge cache, purge on deploy.
  2. Fonts subset and preloaded; fallback metrics matched.
  3. Hero image preloaded; everything else lazy, with dimensions.
  4. No client JavaScript above the fold except the form.
  5. Third-party scripts loaded after interaction or consent; each has an owner.
  6. Budget check in CI on every pull request and every tag container change.
  7. Field data from real sessions reviewed weekly, not just lab scores.

Measuring it

Lab numbers gate the merge. Field numbers, collected from real sessions, tell you whether the lab was honest. When they disagree, the field is right and the throttling profile in CI is wrong. The budget config lives next to the code so a change to the budget is a pull request with a reviewer.

budgets.json
{  "budgets": [    {      "path": "/lp/*",      "resourceSizes": [        { "resourceType": "script", "budget": 90 },        { "resourceType": "total", "budget": 400 }      ],      "resourceCounts": [{ "resourceType": "third-party", "budget": 3 }],      "timings": [        { "metric": "first-contentful-paint", "budget": 1000 },        { "metric": "interactive", "budget": 2000 }      ]    }  ]}

The rule that does the most work

No script runs before first paint unless the page cannot function without it. Almost none can.

Fast pages are not a performance project. They are a habit of not adding things, and the budget makes the habit enforceable by someone who is not in the room. Every page we ship for Grow partners runs under this budget, and the check is part of the handoff.

Put a playbook to work

Every guide here is something we run every week. Bring us the problem and we bring the procedure.