Magic Tools
Developer ToolsBy CooconApril 22, 2026414 views19 min read

Web Performance Optimization: 15 Techniques, Plus a Lighthouse Test of Our Own Site (Mobile LCP 1.9s to 11.8s Across Pages)

Introduction

Google's research established that as page load time increases from 1 second to 3 seconds, bounce rate increases by 32%. At 5 seconds, it jumps 90%. Amazon famously estimated that every 100ms of latency cost them 1% in revenue. These are not hypothetical numbers — they reflect real user behavior captured across billions of sessions.

Beyond user experience, Core Web Vitals are a confirmed Google ranking factor. A slow site costs you both users and organic search visibility simultaneously.

The good news: most performance wins come from a small set of techniques. This guide organizes the 15 most impactful by category, starting with the changes that move the needle most.


Core Web Vitals: What You Are Optimizing For

Google's Core Web Vitals define three user-centric performance metrics. These are what Lighthouse, PageSpeed Insights, and Google Search Console measure and report.

Metric What It Measures Good Needs Work Poor
LCP (Largest Contentful Paint) How quickly the main content loads ≤ 2.5s 2.5–4.0s > 4.0s
INP (Interaction to Next Paint) Responsiveness to user interactions ≤ 200ms 200–500ms > 500ms
CLS (Cumulative Layout Shift) Visual stability (content jumping around) ≤ 0.1 0.1–0.25 > 0.25

INP replaced FID (First Input Delay) in March 2024. It measures the worst interaction latency across the full page visit, making it harder to game and more representative of real interactivity.


Image Optimization (The Biggest Wins)

Images account for 50–70% of total page weight on most websites. This is where the largest gains live.

Technique 1: Use Modern Image Formats

WebP delivers images roughly 25–30% smaller than JPEG at equivalent visual quality, and 30–40% smaller than PNG for photos. AVIF is even more aggressive — often 50% smaller than JPEG — though encoding is slower and browser support is slightly behind WebP.

<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero image">
</picture>

The <picture> element lets browsers select the best format they support, with JPEG as a universal fallback. WebP is now supported in all major browsers including Safari 14+.

Technique 2: Responsive Images with srcset and sizes

Serving a 1920px image to a 375px mobile screen wastes 5–10x the bandwidth needed.

<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w,
          hero-800.jpg 800w,
          hero-1200.jpg 1200w,
          hero-1920.jpg 1920w"
  sizes="(max-width: 600px) 100vw,
         (max-width: 1200px) 80vw,
         1200px"
  alt="Hero image"
>

The browser uses sizes to understand the rendered width in CSS pixels, then selects the appropriate source from srcset based on the device pixel ratio. This requires generating multiple sizes of each image, which image CDNs (Cloudinary, Imgix) or build tools (Sharp, next/image) handle automatically.

Technique 3: Lazy Loading Below-the-Fold Images

<img src="article-photo.jpg" loading="lazy" alt="Article photo">

The loading="lazy" attribute defers image loading until the image approaches the viewport. It requires zero JavaScript and is supported in all modern browsers. Apply it to every image that is not visible in the initial viewport. Never apply it to your LCP image — that counterproductively delays the metric you are trying to improve.

Technique 4: Set Explicit Width and Height to Prevent CLS

<img src="photo.jpg" width="800" height="600" alt="Photo">

Without explicit dimensions, the browser cannot reserve space for the image before it loads, causing content below to jump down — a CLS violation. Setting width and height attributes lets the browser calculate the aspect ratio and reserve space immediately. CSS max-width: 100% still makes it responsive.


Resource Loading

Technique 5: Minify and Bundle CSS/JS

Minification strips whitespace, comments, and shortens variable names. A typical 100KB JavaScript file minifies to 40–50KB. Bundling reduces the number of HTTP requests. Every major build tool handles this: webpack, Vite, esbuild, Rollup, Parcel.

Verify your production build is actually minified by opening DevTools Sources and checking that scripts are not human-readable.

Technique 6: Code Splitting

Shipping your entire JavaScript bundle on every page loads code for features the current user may never trigger. Code splitting divides the bundle into chunks loaded on demand.

// Dynamic import — chunk loaded only when user triggers this feature
const { Chart } = await import('./chart-library.js');

React supports this via React.lazy() and Suspense. Next.js does route-based splitting automatically. Aim for initial JS bundle under 100KB (gzipped) for fast mobile load.

Technique 7: Preload Critical Resources

<!-- Preload the hero image (your LCP candidate) -->
<link rel="preload" href="hero.webp" as="image">

<!-- Preload a critical web font -->
<link rel="preload" href="/fonts/body.woff2" as="font" type="font/woff2" crossorigin>

<!-- DNS prefetch for third-party domains -->
<link rel="dns-prefetch" href="https://analytics.example.com">

preload tells the browser to fetch the resource immediately, at high priority, even if it is not yet in the HTML parser's queue. Use sparingly — preloading too many resources defeats the purpose by competing for bandwidth. Prioritize your LCP element and critical fonts.

Technique 8: Font Optimization

Web fonts are a common source of invisible latency. Two techniques matter most:

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  font-display: swap; /* Show fallback font immediately, swap when loaded */
}

font-display: swap eliminates invisible text during font load (FOIT — Flash of Invisible Text), replacing it with a fallback font immediately. The swap happens when the custom font is ready.

Font subsetting — including only the characters you actually use — reduces font files dramatically. A full Latin + extended font might be 300KB; a subset for English-only content might be 20KB. Tools: pyftsubset, Google Fonts text= parameter.


Network Optimization

Technique 9: CDN for Static Assets

A Content Delivery Network serves static files from edge nodes physically close to users. A visitor in Tokyo loading assets from a server in Virginia experiences 150–200ms of raw latency before a single byte arrives. With a CDN, that drops to under 10ms from a nearby edge node.

All major cloud providers offer CDN services: Cloudflare (exceptional free tier), AWS CloudFront, Fastly, Akamai. Configure your build output, images, and fonts to be served through CDN.

Technique 10: HTTP/2 Multiplexing

HTTP/1.1 browsers open 6 parallel connections per domain. HTTP/2 sends all requests over a single connection simultaneously, eliminating head-of-line blocking. HTTP/2 is enabled at the server or CDN level — no application code changes required.

Verify: open Chrome DevTools → Network tab → right-click any request → add Protocol column. You should see h2 for HTTP/2.

Technique 11: Aggressive Cache Headers

Cache-Control: max-age=31536000, immutable

Fingerprinted assets (with content hash in filename like app.3f4a8b.js) can be cached indefinitely — if the content changes, the filename changes, busting the cache automatically. The immutable directive tells the browser not to revalidate even on hard refresh.

For HTML documents, use Cache-Control: no-cache (forces revalidation but still allows 304 Not Modified responses without retransferring the body).

Technique 12: Brotli Compression

Brotli is a compression algorithm developed by Google that outperforms gzip by 15–25% for text assets (HTML, CSS, JS). Enable it at the server or CDN level. NGINX example:

brotli on;
brotli_comp_level 6;
brotli_types text/html text/css application/javascript;

If your CDN is Cloudflare, Brotli is enabled automatically with zero configuration.


Runtime Performance

Technique 13: Avoid Layout Thrashing

Reading a DOM property like offsetHeight forces the browser to finish all pending style calculations. Alternating reads and writes in a loop causes "layout thrashing" — the browser recalculates layout dozens of times per second.

// Bad: causes layout thrashing
elements.forEach(el => {
  const height = el.offsetHeight; // forces layout
  el.style.height = height * 2 + 'px'; // triggers style change
});

// Good: batch reads, then batch writes
const heights = elements.map(el => el.offsetHeight); // one layout pass
elements.forEach((el, i) => {
  el.style.height = heights[i] * 2 + 'px'; // one style update pass
});

Technique 14: Virtual Scrolling for Long Lists

Rendering 10,000 DOM nodes simultaneously — a contact list, a log viewer, a data grid — creates significant memory pressure and paint cost. Virtual scrolling renders only the items currently visible in the viewport plus a small buffer, swapping nodes as the user scrolls.

Libraries: react-virtual (TanStack Virtual), react-window, @angular/cdk/virtual-scroll. Implementing from scratch is non-trivial; use a library.

Technique 15: Web Workers for CPU-Intensive Tasks

JavaScript runs on the main thread. Heavy computation (parsing large CSVs, image processing, encryption, compression) blocks the main thread and makes the UI unresponsive — directly harming INP.

// main.js
const worker = new Worker('./processor.worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = ({ data: result }) => {
  updateUI(result);
};

// processor.worker.js
self.onmessage = ({ data: { data } }) => {
  const result = expensiveComputation(data); // runs off main thread
  self.postMessage(result);
};

Web Workers run on a separate thread with no access to the DOM. They communicate via message passing. Libraries like comlink make the API ergonomic.


Measurement Tools

Tool Access Best For
Lighthouse Chrome DevTools → Lighthouse tab Complete audit, development iteration
PageSpeed Insights pagespeed.web.dev Real user data (CrUX) + lab data
WebPageTest webpagetest.org Detailed waterfall, multi-location testing
Chrome DevTools Performance tab F12 → Performance Profiling runtime jank and long tasks
Core Web Vitals report Google Search Console Real user data at scale across your site

Run Lighthouse in an incognito window to avoid extension interference. Run PageSpeed Insights on your production URL for data that includes real user measurements from the Chrome User Experience Report (CrUX).


Hands-on test: 24 Lighthouse runs against 4 pages of this site

Background

Every technique above is sound, but the old title of this article promised a speed-up of 50%, and that number was never measured by me. This site is itself a Next.js app with all three page shapes (home, tool pages, article pages) and three third-party scripts (Google Tag Manager, AdSense, Baidu Analytics). So the question is simple: by Google's own yardstick, what does this site score right now, how many seconds is LCP, and which layer is slow? This section answers that, and the number in the title is now a measured one.

Analysis

Three things need to be measured separately:

  1. The score and the three metrics themselves. The Lighthouse performance score is a weighted mix of FCP, LCP, TBT, CLS and Speed Index. The mobile preset simulates a Moto G Power on slow 4G (4x CPU slowdown, 150ms RTT, 1.6Mbps); the desktop preset uses no CPU slowdown, 40ms RTT and 10Mbps. The gap between the two presets on the same page is the answer to "why optimize mobile separately".
  2. Which element is the LCP element. LCP is the render time of the largest content element. If that element is a server-rendered heading, the levers are TTFB and render-blocking resources. If it is a client-side node that only appears after hydration, the levers are JavaScript size and execution time.
  3. How much the third-party scripts cost. GTM, AdSense and Baidu Analytics are the classic "added and forgotten" items. Lighthouse has --blocked-url-patterns, which measures "how much faster without them" without touching a line of code.

One more subtlety that is easy to miss: Lighthouse's default throttling method is simulate. It runs the page unthrottled, records a trace, and then uses the Lantern model to estimate what a slow device on a slow network would experience. The output is a model value, not a measurement. That distinction almost made me write the opposite conclusion in the comparison experiment below.

Approach and what I ruled out

  • Test the production site, not a local dev server. next dev skips production optimizations, so bundle sizes and scores from it are meaningless, and I have no local standalone build identical to what is deployed. Production is the path users take. The price is that the numbers include the network from my machine to the Beijing host; TTFB is listed separately for that reason.
  • Use the Lighthouse 13.4.1 CLI via npx, not the DevTools panel. With 60-plus runs, only the CLI can write every run's full JSON to disk so a script can take medians instead of me copying numbers by hand. PageSpeed Insights was ruled out reluctantly: it runs on Google's machines and adds CrUX field data, which would have been a better control, but the anonymous endpoint had exhausted its daily quota (HTTP 429), and a token from the site's GA service account was rejected for insufficient scope (403). It is marked "not tested" below.
  • 4 pages covering three shapes: the home page (tool card grid), the Markdown editor (heavy client component), the file converter (light client component) and the Chinese edition of this article (pure server-rendered article page). Mobile and desktop, 3 runs each, medians: 24 baseline runs.
  • A real browser UA is required. The site's middleware blocks automation user agents such as curl/ and HeadlessChrome with a 403, and Lighthouse drives headless Chrome. The default settings turned out to pass (see Pitfalls for why), but every formal run passes a real Chrome UA through --emulatedUserAgent to be safe.
  • Two comparison experiments: ① block third-party scripts (GTM / GA / AdSense / DoubleClick / Baidu Analytics / reCAPTCHA), mobile, 4 pages × 3 runs; ② the same before/after with --throttling-method=devtools on the home page and the Markdown page, 3 runs each, to decide whether the simulated result can be trusted.

Procedure

One run looks like this (add --preset=desktop for desktop):

npx --yes lighthouse https://tools.cooconsbit.com/zh \
  --output=json --output-path=raw/home.mobile.1.json --quiet \
  --emulatedUserAgent="Mozilla/5.0 (Linux; Android 11; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36" \
  --chrome-flags="--headless=new --no-first-run"

The 24 baseline runs took 611 seconds. For each run the script first checks that the main document's status code is 200 and runtimeError is empty, then reads the five metrics from audits and sums transfer bytes by resource type from network-requests, and finally takes the median of 3 runs per page and form factor:

Summary table of the 24 baseline Lighthouse runs: 4 pages × mobile/desktop, medians

The comparison runs add --blocked-url-patterns (one flag per domain):

npx --yes lighthouse https://tools.cooconsbit.com/zh --output=json ... \
  --blocked-url-patterns="*googletagmanager.com*" --blocked-url-patterns="*google-analytics.com*" \
  --blocked-url-patterns="*googlesyndication.com*" --blocked-url-patterns="*doubleclick.net*" \
  --blocked-url-patterns="*adtrafficquality.google*" --blocked-url-patterns="*hm.baidu.com*" \
  --blocked-url-patterns="*google.com/recaptcha*" --blocked-url-patterns="*gstatic.com*"

The real-throttling group adds --throttling-method=devtools with everything else unchanged.

Results

Baseline: 4 pages × 2 form factors, median of 3 runs

Page Form factor Perf score FCP LCP TBT CLS Speed Index TTFB Requests Transfer
Article page (zh edition of this post) Desktop 100 0.32s 0.40s 0ms 0 0.66s 109ms 60 1014KB
Article page Mobile 99 1.10s 1.88s 133ms 0 2.16s 110ms 54 1015KB
Home Desktop 99 0.33s 0.83s 0ms 0 0.63s 113ms 62 1033KB
Home Mobile 93 1.25s 2.98s 111ms 0 2.12s 113ms 51 1014KB
File converter Desktop 99 0.29s 0.83s 0ms 0 0.61s 42ms 75 1017KB
File converter Mobile 91 0.98s 3.44s 85ms 0 2.08s 40ms 59 1010KB
Markdown editor Desktop 86 0.30s 2.47s 96ms 0 0.81s 48ms 63 1654KB
Markdown editor Mobile 60 0.97s 11.81s 551ms 0 3.42s 53ms 55 1646KB

Run-to-run noise is small: the home page on mobile scored 88 to 94 across 3 runs with LCP between 2.98s and 3.89s; the Markdown editor on mobile landed at 11.76s to 11.98s, firmly in the "Poor" band all three times. Accessibility is 96 to 97 (91 for the editor on mobile), SEO is 100, and Best Practices is 58 for all 8 combinations with the same three deductions each time: one deprecation warning, 3 third-party cookies, and entries in the DevTools Issues panel, all from the ad and analytics scripts.

Four things to read from the table:

  • CLS is 0 everywhere and TTFB is 40 to 113ms. Layout stability and server response are not the problem. Image dimensions, fonts and SSR are already done on this site, so Techniques 4 and 8 have no headroom here.
  • On the same site, mobile LCP differs by 6x: 1.88s on the article page (the LCP element is the server-rendered <h1>, with only 93ms of render delay) versus 11.81s on the Markdown editor. On desktop those two pages are 0.40s and 2.47s.
  • The Markdown page is slow because of one chunk. Its first-party JavaScript is 20 files, 941KB transferred, 2856KB uncompressed. One file, e39dd30a996a1574.js, is 497KB transferred and 1,478,867 bytes raw: the all-languages refractor syntax-highlighting bundle and the remark / rehype pipeline pulled in by @uiw/react-md-editor. A second 125KB chunk is html2canvas for the image export. The editor is loaded with dynamic(..., { ssr: false }), the LCP element is the editor container <div>, and its render delay is already 1879ms with no throttling on my machine; simulated 4x CPU slowdown plus slow 4G stretch that to 11.8s. This is exactly where Technique 5 (code splitting) should land on this site: load highlighting languages on demand, and fetch html2canvas only when the user clicks export.
  • On the home page and the file converter, the mobile LCP element is the text of the cookie consent banner. The selector is body > div.fixed > div.mx-auto > p.text-sm, with node text "We use cookies to keep you signed in…" (in Chinese). Fetching the home page HTML with curl and searching for that class name gives 0 hits, because the banner component starts with visible set to false and returns null until a useEffect after hydration has read localStorage. So the LCP time equals "JavaScript download + execution + hydration": under real throttling the home page paints FCP at 2.0s and fires DOMContentLoaded at 1.98s, but the batch of Low-priority JS chunks finishes downloading only at 4.3s, and LCP lands at 5.14s with a render delay of 4891ms. A 91-pixel-tall banner stole the home page's LCP from the server-rendered content. The fix is straightforward: server-render the banner and hide it with CSS, or make it smaller than the above-the-fold heading.

What the third-party scripts cost: before and after blocking (mobile, median of 3)

Page Perf score LCP TBT Requests Transfer
Article page 99 → 99 1.88s → 2.16s 133ms → 3ms 54 → 38 1015KB → 410KB
Home 93 → 83 2.98s → 4.61s 111ms → 4ms 51 → 35 1014KB → 411KB
File converter 91 → 84 3.44s → 4.48s 85ms → 16ms 59 → 43 1010KB → 405KB
Markdown editor 60 → 64 11.81s → 8.60s 551ms → 446ms 55 → 39 1646KB → 1047KB

The three third parties add up to roughly 600KB per page: GTM 316KB, Google ads 220 to 245KB, adtrafficquality 22KB, Baidu Analytics 12KB. After blocking, TBT drops to near zero and transfer shrinks by 60%, both as expected. But LCP on the home page and the file converter got worse by 1 to 1.6 seconds, and the scores dropped 7 to 10 points. My first guess was that the LCP element had changed. The JSON says no: before and after, the LCP element is the same cookie text, and the unthrottled observed LCP on my machine moved only from 380ms to 476ms. What changed was Lantern's estimate.

So I re-ran the same comparison with devtools throttling:

Lantern simulation vs devtools throttling: LCP before and after blocking third-party scripts on the home page and the Markdown page

Page Throttling Perf score LCP baseline → blocked Change
Home simulate 93 → 83 2.98s → 4.61s +1.63s (+55%)
Home devtools 79 → 83 5.13s → 4.45s −0.69s (−13%)
Markdown editor simulate 60 → 64 11.81s → 8.60s −3.21s (−27%)
Markdown editor devtools 53 → 57 12.25s → 9.35s −2.89s (−24%)

Under real throttling the direction is right: the home page gets 13% faster, the Markdown page 24%, and home-page TBT goes from 56ms to 0. So the real cost of this site's third-party scripts is "10 to 30% of LCP and all of TBT", not the negative number the model produced. A side observation: the devtools home-page LCP is 5.13s versus 2.98s simulated, because real throttling adds a full 150ms round trip to every TLS handshake and every request, while Lantern's model is more optimistic about small requests. Absolute values from the two methods cannot be compared with each other; only before/after deltas within one method mean anything.

PSI / CrUX field data: not tested. The anonymous endpoint returned HTTP 429 and the service-account token returned 403. Every number in this section is lab data with no real-user samples.

Pitfalls

  • Deltas from Lantern-simulated throttling are not experimental results. The default --throttling-method=simulate outputs model estimates. For a comparison that changes the shape of the request graph, such as blocking third-party scripts, the model gave the home page +55% LCP while real throttling measured −13%. Any before/after comparison should be re-checked with devtools throttling at least once; simulate is fine for single absolute scores and low-noise repeated measurements.
  • When a site blocks by UA, Lighthouse's defaults actually pass; only --no-emulated-user-agent gets the 403. The tip I was given beforehand said the default UA contains HeadlessChrome and gets blocked. I tested 5 settings: default mobile emulation, --preset=desktop, and an explicit --emulatedUserAgent with a real Chrome UA all got a 200 for the main document, because Lighthouse overrides the UA by default with a Moto G or Mac Chrome string (visible in configSettings.emulatedUserAgent). Only --emulatedUserAgent="curl/8.7.1" and --no-emulated-user-agent got 403 with ERRORED_DOCUMENT_REQUEST. Do not guess whether you were blocked; read the statusCode of the first entry in network-requests in the JSON. Main-document status code under five UA settings: defaults pass, disabling UA emulation gets 403
  • Some runs of the same page finish in 12 seconds, others in 55. The difference is the GA /g/collect request: my machine connects directly with no proxy, that request never returns, and Lighthouse waits for the 45-second maxWaitForLoad ceiling before wrapping up. All 6 home-page runs hit this, 4 Markdown runs did, and the other pages, which never sent the request, finished in 7 to 12 seconds. The metrics are unaffected (FCP and LCP are long decided by then), but batch runs need to budget for the worst case.
  • environment.networkUserAgent does not reflect --emulatedUserAgent. I passed Chrome/140 and the JSON field says Chrome/152 (the host Chrome version). That field records Lighthouse's default string; the value actually in effect is in configSettings.emulatedUserAgent. The curl-UA run's 403 proves the flag works.
  • Lighthouse 13 audit ids differ from older versions. largest-contentful-paint-element, third-party-summary and mainthread-work-breakdown are gone; the LCP element and its phases live in lcp-breakdown-insight, third parties in third-parties-insight. Run Object.keys(lhr.audits) before writing a summary script instead of working from memory.
  • The PageSpeed Insights API is practically unusable without a key. Anonymous calls share one daily quota, already exhausted (429) that day; the GA service-account token does not help either (PSI only accepts API keys and returned 403 for insufficient scope). Real-user CrUX data requires creating a key in Google Cloud first.
  • If you cannot find an element in the SSR HTML, do not assume it was server-rendered. For the cookie banner I first assumed "the text must be SSR, so the slow LCP is the font's fault"; the font audit was in fact a perfect score. Only after curl found 0 hits for the class name in the HTML did it click that the element is mounted after hydration. To decide which layer an LCP element belongs to, check whether it is in the raw HTML first.

The raw JSON (raw/, raw-blocked/, raw-devtools-*/, raw-noua/), the run scripts (run-lh.mjs / run-blocked.mjs / run-devtools.mjs) and the summary scripts (summarize.mjs / compare-devtools.mjs / ua-check.mjs) are all in the repository under tmp/2026-09-16-cwv-lighthouse/, and every number in the tables above can be traced back there.


FAQ

What is a good LCP score? Under 2.5 seconds for the 75th percentile of your users (meaning 75% of visits experience LCP under that threshold). Google scores pages as "Good," "Needs Improvement," or "Poor" — the Good threshold is the target. Focus first on your LCP element: it is almost always a hero image, a large heading, or a video thumbnail.

Do JavaScript frameworks hurt performance? They can, but it is usually implementation quality rather than the framework itself. React, Vue, Angular, and Svelte all have production sites with excellent Core Web Vitals. The risks specific to frameworks: large initial bundle size, client-side rendering delaying LCP, and hydration causing INP spikes. Server-side rendering or static generation eliminates the LCP delay. Techniques 5, 6, and 13 address the rest.

Should I prioritize mobile or desktop performance? Mobile. Google uses mobile-first indexing, meaning your mobile performance score is what affects search ranking. Mobile devices have slower CPUs, slower networks, and less memory than desktops. Achieving good Core Web Vitals on mobile typically ensures excellent desktop performance automatically. Test on a real mid-range Android device (or Lighthouse's mobile simulation) rather than your development machine.


Conclusion

Performance optimization has a natural order of returns: image optimization delivers disproportionate gains for most sites because images dominate page weight. Network optimization (CDN, caching, HTTP/2) is often free with modern infrastructure. Runtime optimization matters most for interactive applications.

Start by running Lighthouse on your current production site. Look at the opportunities section — it prioritizes recommendations by estimated time savings and is almost always correct about where to focus first. Address the biggest opportunities, deploy, measure again, and repeat.

The 15 techniques in this guide are not exotic; they are industry standard. Most can be implemented in a day. But measure before you pick one: on this very site, mobile LCP ranges from 1.88s to 11.81s across pages, the slow page is slow because of one 1.48MB highlighting bundle, and the fast page's LCP element turned out to be the cookie banner. Neither of those is something you would guess from a checklist.

Related Articles

Image Compression in 2026: Best Practices for Web Performance

Images account for 50%-70% of page weight and are the top cause of LCP timeouts and conversion loss. This 2026 playbook covers AVIF/WebP/JPEG XL format selection, quality sweet spots, responsive srcset, CDN integration, SEO and Core Web Vitals — plus a real case study slimming a blog homepage by 80%.

web-performancecore-web-vitals+3
utilityApr 21, 20265 min
525

Claude API Advanced: Batching, Caching, and Cost Optimization

Explore advanced Claude API features: Message Batches API for 50% cost reduction, Prompt Caching to save 90% on input costs for long contexts, and other optimization strategies.

optimizationadvanced+3
claudeApr 13, 20262 min
288

GitHub Native Stacked PR Tutorial: How to Use the gh stack Command, How to Merge, and How to Convert Old Branches

GitHub made Stacked PR a native feature and opened public preview on July 30, 2026: split a large change into a series of stacked small PRs, review layer by layer, and merge the entire stack with one click. This article is based on the official changelog and gh-stack documentation: five-minute quickstart (init/add/push/submit), review and merge rules (can you merge only one?), how to convert existing branches into a stack, daily maintenance with rebase/sync, and answers to frequent questions like pricing and Windows support.

developerSep 4, 20265 min
670

Containers in 1.5MB: Kern kills the daemon

Kern squeezes the entire container and resource runtime into a 1.5MB single binary with no resident daemon. Here's the daemon-less process model, what 1.5MB actually buys you, why this is a decade of evolution since Docker, and why you shouldn't migrate yet.

developerAug 26, 20262 min
264

Published by Magic Tools