A sub-1s studio site: the performance budget we ship to
How we ship studio websites that paint first content under one second on a mid-range phone over 4G — the budget we hold ourselves to, what gets cut, and the small architectural decisions that matter most for Core Web Vitals.
There is a particular kind of studio website — the one where each scroll triggers a parallax shimmer, a font swap, an autoplay reel that buffers for three seconds, and a cursor that grows fangs on hover — that loads in eight seconds on a mid-range phone and feels broken on every connection that isn't fiber. We don't ship those. The brief we hold ourselves to is brutal and simple: first contentful paint in under a second, on a Pixel 6a, over a throttled 4G connection.
Meeting that brief is not about heroic optimization. It's about a small number of architectural choices that compound, and a much larger number of things we don't do. This article is the budget we work to, the decisions behind it, and the field measurements that prove the budget held. If you're shipping a studio site, agency site, or any portfolio-shaped surface, this is a real-world target you can copy almost wholesale.
The budget, in numbers
Every studio site we ship targets these numbers on a Pixel 6a over Fast 3G in Chrome DevTools throttling. Reality on a 4G connection is faster; we use Fast 3G as the worst-case lower bound. We measure with Lighthouse and with real-user metrics in production via Vercel Analytics.
- —Largest Contentful Paint (LCP): under 1.2s in lab, under 1.8s p75 in the field. Google's 'good' threshold is 2.5s.
- —First Contentful Paint (FCP): under 1.0s in lab.
- —Cumulative Layout Shift (CLS): under 0.05. Google's 'good' threshold is 0.1; we hold ourselves to half that.
- —Interaction to Next Paint (INP): under 100ms. Google's 'good' threshold is 200ms.
- —Total transfer size on first load: under 150KB compressed, including HTML, CSS, fonts, JS, and the hero image.
- —JavaScript bundle on first load: under 80KB compressed.
If a page misses any of these, we treat it as a P1 bug. Not 'something to come back to.' A P1 we fix before the site ships.
Why so strict?
Two reasons. First, Core Web Vitals are a ranking factor for Google. Not a tiebreaker — a ranking factor. Sites that meet the thresholds are eligible for the page-experience boost; sites that don't are penalized at the margin. For a studio competing for narrow keyword clusters, the margin matters.
Second, perceived performance is a brand signal. A site that loads instantly reads as competent. A site that hangs while a tracking pixel resolves reads as not. For a studio whose entire pitch is 'we ship considered, modern work,' the site has to demonstrate that pitch on first contact. The site is the brief.
Decision 1: Statically render everything
The single biggest performance decision is rendering strategy. We use Next.js with the App Router, and every page on our studio sites is statically rendered at build time. No runtime database lookups. No per-request server work beyond the edge function that serves the HTML. The whole site is files on a CDN.
What this buys you, concretely: TTFB at the edge is whatever your CDN says it is — typically 30–80ms anywhere on the planet. The first byte of HTML arrives before the browser has finished its TLS handshake on a server-rendered alternative. Everything downstream — paint, interactivity, the next page — starts that much earlier.
The tradeoff is that content changes require a deploy. For a studio site that updates monthly, a deploy is a non-event — it takes 90 seconds on Vercel. For a publication that updates hourly, the math is different and ISR (Incremental Static Regeneration) is the right tool. But studio sites do not update hourly.
Decision 2: One web font, two weights
Custom fonts are the single largest controllable expense in first paint. A web font subset for a single weight is typically 25–40KB compressed. A site that loads three weights of two families ships 150KB of fonts before the first byte of content paints. We ship one family in two weights, served via next/font's self-hosted, hashed delivery — no Google Fonts third-party request, no DNS lookup to a font CDN.
We also accept the consequence: there's no italic, no thin, no extrabold. Every typographic distinction has to be made with size, color, tracking, or weight contrast between the two weights we have. This is a creative constraint that pays for itself. Sites that need eight weights tend to be hiding poor hierarchy under typographic variety; sites that work in two weights have to earn their hierarchy structurally.
Decision 3: Hero image budget — and how to spend it
On a studio site, the LCP is almost always the hero. So the LCP budget is the hero budget. We give the hero a strict ceiling: 80KB compressed at the desktop breakpoint, 40KB on mobile. Anything larger is a creative challenge to solve in image selection or treatment, not an excuse to overspend the budget.
Four practices that keep us within the budget:
- 01Use AVIF where supported, with a WebP fallback. AVIF is typically 30–40% smaller than WebP at equivalent quality.
- 02Serve responsive sizes via the next/image component. Phones never download the desktop hero; desktops never download the retina-2x version unless the screen is actually retina.
- 03Set explicit width and height on every image. This is what prevents layout shift — the browser reserves the right number of pixels before the image arrives.
- 04Preload the hero image. <link rel="preload" as="image" href="…"/> in the head. Costs one line, saves 200–400ms on LCP because the browser starts the image request before parsing the rest of the page.
Decision 4: JavaScript as the last resort
Every kilobyte of JavaScript is a kilobyte that has to download, parse, and execute on the main thread before the page is interactive. We treat the JS budget as scarce and hold to two rules:
- —Server components by default. Only mark a component 'use client' if it actually needs interactivity. The default in React Server Components is the right default for content-heavy sites.
- —No third-party scripts on first load. No analytics that ships 80KB of vendor code. No tag managers. Analytics runs lazily after the page is interactive — we use Vercel Analytics, which ships about 1KB.
The result is that our studio sites typically ship under 80KB of compressed JS on first load — most of which is React itself. The interactive layer (nav menu, language toggle, contact form) is a few kilobytes.
Decision 5: CSS architecture that doesn't ship layout-shift bugs
Cumulative Layout Shift is the easiest Core Web Vital to fail by accident and the easiest to fix on purpose. The four habits that keep CLS near zero:
- 01Reserve space for everything that arrives late. Images, fonts, iframes, ads — all get an aspect-ratio container with explicit dimensions.
- 02Avoid CSS that depends on the font being loaded. Use system font fallbacks during the swap window, with font-display: swap. Pick a fallback whose metrics match the web font so the swap is invisible.
- 03Don't inject content above the fold post-load. Banners, cookie notices, and announcement bars that appear after first paint are a layout shift waiting to happen. If you need them, render them in the initial HTML and animate them in.
- 04Test on slow networks. CLS issues that don't show on fiber show on 3G because the late-arriving resource arrives latest.
Decision 6: The animation budget
Restraint in motion is its own performance optimization. Every animation that runs on the main thread risks an INP regression — a single janky 250ms animation on click pushes the whole site out of the 'good' INP bucket. Our rules:
- —Only animate transform and opacity. Both are GPU-composited and don't trigger layout.
- —Reveal animations are CSS, not JavaScript. We use an IntersectionObserver to add a className; the actual animation is a transition.
- —No scroll-linked animations. They sound elegant and ship terrible scroll performance on mid-range devices.
- —No autoplaying video on the hero. If video is needed, lazy-load it below the fold or behind an interaction.
Decision 7: The accessibility / performance overlap
Most accessibility wins are also performance wins. Semantic HTML is smaller than div-soup. Native focus rings render faster than custom JS focus management. Skip-link headers obviate keyboard-trap fixes that ship JavaScript. We design and build for accessibility first; performance follows for free.
The one place performance and accessibility tension up is animation. Some users need motion; others get nauseous. We respect prefers-reduced-motion and ship every reveal animation with a fallback that's a fade — not a translate.
Measuring in production
Lab metrics from Lighthouse are necessary but not sufficient. The gold standard is real-user monitoring (RUM) — what actual visitors on actual devices experience. We use Vercel Analytics for Web Vitals because it ships free with the platform and reports field metrics segmented by route, device, and geography.
What we look at weekly:
- —p75 LCP per route. If any route p75 exceeds 1.8s, we investigate.
- —INP regressions. INP is the sneakiest metric — it can degrade silently as JavaScript grows.
- —CLS by route. CLS regressions usually trace to a single late-loading element.
- —First-load JS by route, tracked via the bundle analyzer in CI. We fail builds that exceed the budget.
What we don't do
Equally important — the things we deliberately leave out, despite being industry-standard:
- —No SPA-style client-side routing on a small site. The full document fetch is faster than the bundle delta on a 4G connection. We let the browser navigate.
- —No service worker. The complexity isn't worth it on a sub-10-page site.
- —No image hosting service that injects a 30KB SDK. We ship images from the same domain as the site, optimized at build time.
- —No 'smart' lazy-load library. The native loading="lazy" attribute does the job in 0KB.
- —No prefetching of every link on hover. Next.js prefetches by default in production; we leave that on and let the framework decide.
If you're scoping a site and want a performance audit on what you have, or you want one built to this budget from the start, hello@neptay.com.
Anatomy of a brand × creator partnership: how the deals are scoped, priced, and measured
A deep walk-through of how brand × creator partnerships really work — sourcing, pricing, contracts, production, and post-campaign measurement, with specific deal mechanics from international markets including Türkiye.
EngineeringBuilding a bilingual Next.js App Router site: i18n, hreflang, and structured data done right
How we ship bilingual sites on the Next.js App Router without bolting i18n on as an afterthought — routing, hreflang, structured data, and the middleware tricks that keep both languages first-class.