{"id":19,"date":"2026-06-06T21:48:40","date_gmt":"2026-06-06T21:48:40","guid":{"rendered":"https:\/\/softwareproduction.eu\/wordpress\/?p=19"},"modified":"2026-06-07T01:22:02","modified_gmt":"2026-06-07T01:22:02","slug":"ultimate-performance-optimization-chasing-that-100-lighthouse-score","status":"publish","type":"post","link":"https:\/\/softwareproduction.eu\/?p=19","title":{"rendered":"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Masterclass in building a modern Nuxt 4 stack that hits Lighthouse 97\u2013100 on mobile without going \u201czero\u2011JS\u201d.<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Performance Is an Architecture Decision<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In large enterprise applications, performance rot rarely starts with \u201cslow code.\u201d It starts when the architecture cannot absorb new features without stacking more JavaScript, more requests, and more complexity on the critical path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Most teams:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Build features fast.<\/li>\n<li>Ship.<\/li>\n<li>Only care about performance when Lighthouse scores become embarrassing.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">By that point, slow patterns are baked into the system. The only options left are tactical band-aids.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The mindset here is inverted: <strong>define performance targets first, then design the architecture so those targets are the default outcome.<\/strong> Optimization becomes a design constraint, not a cleanup phase.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Understanding the Metrics That Actually Matter<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Lighthouse simulates a real user on a mid-range phone over throttled 4G. It measures:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>How quickly something meaningful paints.<\/li>\n<li>How soon the page feels interactive.<\/li>\n<li>Whether the layout jumps around while loading.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The <em>Performance<\/em> score (0\u2013100) is just a weighted composite of <strong>LCP<\/strong>, <strong>TBT<\/strong>, <strong>CLS<\/strong>, <strong>FCP<\/strong>, and <strong>Speed Index<\/strong>. The score is a proxy; the real goal is to keep each metric in the \u201cgood\u201d range where user-perceived performance and bounce rates are measurably better.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The targets:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Metric<\/th><th>Target<\/th><th>Why<\/th><\/tr><\/thead><tbody><tr><td><strong>LCP<\/strong> (Largest Contentful Paint)<\/td><td>< 2.5 s<\/td><td>Core Web Vitals \u201cgood\u201d threshold<\/td><\/tr><tr><td><strong>CLS<\/strong> (Cumulative Layout Shift)<\/td><td>< 0.1<\/td><td>Prevents content jumps<\/td><\/tr><tr><td><strong>TBT<\/strong> (Total Blocking Time)<\/td><td>< 200 ms<\/td><td>Page feels interactive quickly<\/td><\/tr><tr><td><strong>Lighthouse Performance<\/strong><\/td><td>\u2265 95 (mobile)<\/td><td>Competitive benchmark<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Everything that follows exists to hit these numbers by design.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 1: SSR as the Performance Foundation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Server-Side Rendering is the <strong>foundational<\/strong> decision. With SSR, the server returns fully-rendered HTML for the initial request, so the browser can paint meaningful content before any JavaScript executes.<\/p>\n\n\n\n<pre class=\"mermaid\">sequenceDiagram\n    participant U as User\n    participant B as Browser\n    participant S as Server\n    participant J as JS Runtime\n\n    U-&gt;&gt;B: Request page\n    B-&gt;&gt;S: HTTP request\n    S--&gt;&gt;S: Render complete HTML (SSR)\n    S--&gt;&gt;B: Return HTML\n    B--&gt;&gt;U: Paint content (FCP, meaningful content)\n    Note over B,U: Page visible, usable without JS\n\n    B--&gt;&gt;J: Load JS in background\n    J--&gt;&gt;B: Hydrate page\n    Note over B,U: Page becomes fully interactive<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In a client-rendered SPA, the browser first downloads a JS bundle, parses it, executes it, and then finally renders content. SSR shortcuts straight to \u201ccontent on screen,\u201d giving you an LCP advantage that no client-rendered approach can match.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is what makes the later layers\u2014deferred hydration, same-origin images, lazy third-party scripts\u2014safe: the user always has real content immediately.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 2: Multi-Tier Caching \u2014 Eliminate Redundant Work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rendering a page from CMS or backend data on every request is wasteful. The content often barely changes; the work repeats constantly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>multi-tier cache<\/strong> strips out redundant work at every layer:<\/p>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    R[Request \/products\/premium] --&gt; T1[\"Tier 1: Page Data Cache&lt;br\/&gt;(Redis + in-memory LRU)&lt;br\/&gt;Key: page-data:\/products\/premium\"]\n    T1 -- Hit --&gt; P1[Return cached payload]\n    T1 -- Miss --&gt; T2[\"Tier 2: Apollo Server Cache&lt;br\/&gt;(per GraphQL operation, LRU)\"]\n    T2 -- Hit --&gt; P2[Return cached response]\n    T2 -- Miss --&gt; T3[\"Tier 3: Apollo Client Cache&lt;br\/&gt;(InMemoryCache, per session)\"]\n    T3 -- Hit --&gt; P3[Return from memory]\n    T3 -- Miss --&gt; T4[\"Tier 4: Nuxt useState&lt;br\/&gt;(SSR payload hydration)\"]\n    T4 --&gt; H[\"Data serialized into HTML&lt;br\/&gt;Client hydrates without fetch\"]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Tier 1: Page Data Cache<\/strong> is the heavy hitter. Each page is composed from many CMS entries via GraphQL\u2014the \u201cstub-and-load\u201d pattern. Instead of re-running all those queries, the cache stores the fully assembled payload for a URL path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On a cache hit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No CMS calls.<\/li>\n<li>No GraphQL roundtrips.<\/li>\n<li>Just a sub-millisecond payload lookup and SSR render.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Invalidation is webhook-driven: content change \u2192 webhook \u2192 Redis Pub\/Sub \u2192 replicas clear affected keys. You get the dynamic behavior of a headless CMS with the speed profile of a static site.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 3: Deferred Hydration \u2014 Interactivity on Demand<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">SSR gives you a fast first paint. But if the browser immediately downloads and executes a multi-megabyte JS bundle for hydration, the main thread stalls, TBT explodes, and the page <em>feels<\/em> sluggish despite looking ready.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The strategy:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">> <strong>Defer hydration until the user interacts.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A Nitro <code>render:html<\/code> plugin rewrites the HTML in three steps:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Collect &#038; strip <code><link rel=\"modulepreload\"><\/code><\/strong>  <\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">   Nuxt emits dozens of preload hints, causing eager download\/parse of ~90 JS chunks. The plugin:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Captures these URLs into a JS array.<\/li>\n<li>Removes the tags from the HTML, preventing early work.<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Replace the entry <code><script type=\"module\"><\/code><\/strong>  <\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">   The standard entry script is removed and replaced with a tiny inline script that starts hydration only on <strong>first interaction<\/strong> (scroll, click, touch, mousemove, keydown) or after a <strong>4-second timeout<\/strong>.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>On trigger: prefetch \u2192 yield \u2192 load<\/strong>  <\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">   When hydration should start:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Inject <code><link rel=\"prefetch\"><\/code> hints for the top ~30 chunks (warm HTTP cache).<\/li>\n<li>Yield back to the browser (<code>scheduler.yield()<\/code> or <code>setTimeout(0)<\/code> fallback).<\/li>\n<li>Append the real module entry script; ES module imports then load from the warmed cache.<\/li>\n<\/ul>\n\n\n\n<pre><code class=\"language-html\">&lt;!-- After plugin rewrite (simplified) --&gt;\n&lt;script&gt;\n  (function() {\n    var done = 0\n    var urls = [\"\/_nuxt\/chunk-abc.js\", \"\/_nuxt\/chunk-def.js\", \/* ...top 30... *\/]\n\n    function go() {\n      if (done) return; done = 1\n      events.forEach(function(e) { removeEventListener(e, go) })\n\n      \/\/ Prefetch critical chunks\n      urls.forEach(function(u) {\n        var l = document.createElement('link')\n        l.rel = 'prefetch'; l.as = 'script'; l.href = u\n        document.head.appendChild(l)\n      })\n\n      \/\/ Yield, then load entry from warm cache\n      ;(scheduler &amp;&amp; scheduler.yield ? scheduler.yield() : Promise.resolve()).then(function() {\n        var s = document.createElement('script')\n        s.type = 'module'\n        s.src = '\/_nuxt\/entry.js'\n        document.body.appendChild(s)\n      })\n    }\n\n    var events = ['scroll','click','touchstart','mousemove','keydown']\n    events.forEach(function(e) {\n      addEventListener(e, go, { capture: true, once: true, passive: true })\n    })\n\n    \/\/ Safety net for non-interacting visitors \/ bots\n    setTimeout(go, 4000)\n  })()\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <strong>4-second delay<\/strong> is tuned to Lighthouse:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>TBT is measured between FCP and TTI.<\/li>\n<li>TTI requires a 5-second quiet window.<\/li>\n<li>Keeping the main thread almost idle for 4 seconds after FCP drives TBT \u2248 0 and TTI \u2248 FCP.<\/li>\n<li>If a real user interacts earlier, hydration fires immediately.<\/li>\n<\/ul>\n\n\n\n<pre class=\"mermaid\">sequenceDiagram\n    participant B as Browser\n    participant P as Plugin Script\n    participant N as Network\n\n    Note over B: SSR HTML painted (FCP)\n    B-&gt;&gt;P: Page load complete\n    P--&gt;&gt;P: Collect modulepreload URLs&lt;br\/&gt;Remove preload tags\n\n    rect rgba(200,200,200,0.2)\n    Note over B,P: Wait for user interaction&lt;br\/&gt;or 4s timeout\n    B-&gt;&gt;P: scroll\/click\/touch\/move\/keydown\n    end\n\n    P-&gt;&gt;N: Inject prefetch links for top ~30 chunks\n    N--&gt;&gt;B: Warm HTTP cache with JS chunks\n\n    P-&gt;&gt;B: Yield to main thread&lt;br\/&gt;(scheduler.yield \/ Promise)\n    B--&gt;&gt;P: Idle slot available\n\n    P-&gt;&gt;B: Append real module entry script\n    B-&gt;&gt;N: Load entry from warm cache\n    N--&gt;&gt;B: JS executes, hydration starts\n    Note over B: Page becomes interactive<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Measured impact in a large Nuxt application:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Metric<\/th><th>Before<\/th><th>After<\/th><th>Delta<\/th><\/tr><\/thead><tbody><tr><td><strong>FCP<\/strong><\/td><td>~2.5 s<\/td><td>< 1.0 s<\/td><td>\u221260%<\/td><\/tr><tr><td><strong>TBT<\/strong><\/td><td>~500 ms<\/td><td>< 100 ms<\/td><td>\u221280%<\/td><\/tr><tr><td><strong>Speed Index<\/strong><\/td><td>~3.5 s<\/td><td>< 1.5 s<\/td><td>\u221257%<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> This only works because SSR ensures a fully rendered page. In a pure SPA, deferring JS would show a blank screen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 4: Same-Origin Image Proxy \u2014 Your Biggest LCP Win<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On real-world pages, the <strong>LCP element is almost always an image<\/strong>, usually the hero. On mobile 4G, opening a fresh HTTPS connection to a third-party image CDN costs <strong>250\u2013430 ms<\/strong> in DNS, TCP, and TLS before a single byte of image data arrives.<\/p>\n\n\n\n<pre><code class=\"language-text\">Connection Overhead for Third-Party CDN (mobile 4G):\n\n  DNS resolution:       50\u201380 ms\n  TCP handshake:       100\u2013150 ms\n  TLS negotiation:     100\u2013200 ms\n  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  Total overhead:      250\u2013430 ms<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Your own origin already has an open HTTP\/2 connection. Its overhead is essentially zero.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Strategy: Rewrite Image URLs to Same-Origin<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You keep using your CMS image CDN\u2014but hide it behind a same-origin proxy so the browser can reuse the existing connection.<\/p>\n\n\n\n<pre class=\"mermaid\">sequenceDiagram\n    participant U as User\n    participant BR as Browser\n    participant O as Your Origin&lt;br\/&gt;(www.example.com)\n    participant P as Proxy Route&lt;br\/&gt;(\/_cfi\/...)\n    participant C as Image CDN\n\n    Note over BR: Initial HTML from origin&lt;br\/&gt;contains rewritten image URLs&lt;br\/&gt;(\/_cfi\/media\/hero.jpg)\n    U-&gt;&gt;BR: Request page\n    BR-&gt;&gt;O: GET \/page\n    O--&gt;&gt;BR: HTML with \/_cfi\/media\/hero.jpg\n\n    BR-&gt;&gt;O: GET \/_cfi\/media\/hero.jpg&lt;br\/&gt;(reuses HTTP\/2 connection)\n    O-&gt;&gt;P: Route to Nuxt proxy\n    P-&gt;&gt;C: GET https:\/\/images.cdn-provider.net\/media\/hero.jpg\n    C--&gt;&gt;P: Image bytes + cache headers\n    P--&gt;&gt;O: Stream response\n    O--&gt;&gt;BR: Stream image to browser\n    Note over BR: LCP image loads over existing connection<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Measured impact on Lighthouse mobile (simulated 4G) in a production-scale app:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Metric<\/th><th>Before<\/th><th>After<\/th><th>Delta<\/th><\/tr><\/thead><tbody><tr><td><strong>LCP<\/strong><\/td><td>3,152 ms<\/td><td>2,558 ms<\/td><td><strong>\u2212594 ms (\u221219%)<\/strong><\/td><\/tr><tr><td><strong>Performance Score<\/strong><\/td><td>92<\/td><td>97<\/td><td><strong>+5 points<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">All from shaving protocol overhead. Same bytes, same compression; just better use of the network.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Component 1: URL Rewriting with <code>render:html<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A Nitro <code>render:html<\/code> plugin rewrites third-party CDN URLs to same-origin paths in the <strong>final SSR HTML string<\/strong>:<\/p>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    A[Input SSR HTML] --&gt; B[\"Search for CDN URLs&lt;br\/&gt;e.g. https:\/\/images.cdn.net\/media\/\"]\n    B --&gt; C[\"Replace prefix with&lt;br\/&gt;\/_cfi\/media\/\"]\n    C --&gt; D[\"Updated HTML output&lt;br\/&gt;&lt;img src=#quot;\/_cfi\/media\/hero.jpg#quot;&gt;\"]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Because this happens at string level:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It catches <code>src<\/code>, <code>srcset<\/code>, inline <code>background-image<\/code>, and anything else in the HTML output.<\/li>\n<li>The CMS remains unaware; it still stores its original URLs.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Component 2: Proxy Server Route<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A Nuxt server route handles all proxied image requests:<\/p>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    R[Request \/_cfi\/media\/hero.jpg] --&gt; E[Extract path: media\/hero.jpg]\n    E --&gt; F[\"Build upstream URL&lt;br\/&gt;https:\/\/images.cdn.net\/media\/hero.jpg\"]\n    F --&gt; G[Fetch upstream image]\n    G --&gt; H[\"Set Cache-Control:&lt;br\/&gt;public, max-age=31536000, immutable\"]\n    H --&gt; I[Stream response back to client]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The proxy is transparent: same bytes, content type, and long-lived cache headers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Component 3: Nginx Edge Caching<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In front of Nuxt, an Nginx reverse proxy sits behind a global edge cache (e.g., a managed CDN \/ application gateway). Together they turn the proxy into a \u201cwarm-up-only\u201d path:<\/p>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    U[Browser] --&gt; ECDN[Edge CDN POP]\n    ECDN --&gt;|\"MISS (first time)\"| NX[Nginx]\n    NX --&gt;|\"MISS (first time)\"| NUXT[Nuxt Proxy Route]\n    NUXT --&gt; IMG[Upstream Image CDN]\n    IMG --&gt; NUXT\n    NUXT --&gt; NX\n    NX --&gt; ECDN\n    ECDN --&gt; U\n\n    ECDN -.cache HIT on next requests.-&gt; U\n    NX -.cache HIT after warm-up.-&gt; ECDN<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once warmed, production traffic almost never touches the Nuxt route. Operationally, the proxy is invisible.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Handling Multiple CDNs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Different services, different CDNs\u2014same pattern:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>CDN Origin<\/th><th>Proxy Path<\/th><th>Server Route<\/th><\/tr><\/thead><tbody><tr><td>CMS images<\/td><td><code>\/_cfi\/*<\/code><\/td><td><code>server\/routes\/_cfi\/[...path].get.ts<\/code><\/td><\/tr><tr><td>Asset library images<\/td><td><code>\/_ffi\/*<\/code><\/td><td><code>server\/routes\/_ffi\/[...path].get.ts<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Adding a new origin = one server route + one regex in the render plugin.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why Not Just <code><link rel=\"preconnect\"><\/code>?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>preconnect<\/code> helps, but it cannot eliminate the cost:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;link rel=\"preconnect\" href=\"https:\/\/images.cdn-provider.net\"&gt;<\/code><\/pre>\n\n\n\n<pre class=\"mermaid\">flowchart LR\n    A[No optimization] --&gt;|\"250\u2013430 ms&lt;br\/&gt;DNS+TCP+TLS\"| LCP1[LCP image starts late]\n    B[\"rel='preconnect'\"] --&gt;|\"50\u2013100 ms&lt;br\/&gt;reduced handshake\"| LCP2[LCP image starts sooner]\n    C[Same-origin proxy] --&gt;|\"0 ms&lt;br\/&gt;connection reuse\"| LCP3[LCP image starts fastest]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For LCP-critical images, the proxy is worth the extra implementation effort.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 5: Font Loading Strategy \u2014 Performance Without CLS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Custom fonts are silent performance killers:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>FOIT<\/strong> (Flash of Invisible Text): text stays invisible while fonts download.<\/li>\n<li><strong>CLS from font swapping<\/strong>: when the custom font finally applies, text reflows.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The strategy combines three techniques:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>font-display: optional<\/code><\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  The browser gives the font a very short window (~100 ms). If it does not arrive in time, the fallback is used for that navigation. Result:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No blocking of text.<\/li>\n<li>No flash (it never swaps late in the same load).<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Metrically tuned fallbacks with <code>size-adjust<\/code><\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  Use a local system font as a fallback and adjust its metrics to match the custom font:<\/p>\n\n\n\n<pre><code class=\"language-css\">  @font-face {\n    font-family: 'Inter Fallback';\n    src: local('System UI');\n    size-adjust: 107%;\n    ascent-override: 92%;\n    descent-override: 26%;\n    line-gap-override: 0%;\n  }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">  When the real font loads on subsequent navigations, layout does not move\u2014CLS from fonts drops effectively to zero.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Preload critical weights<\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  Preload the most-used weights (e.g., regular, medium):<\/p>\n\n\n\n<pre><code class=\"language-html\">  &lt;link rel=\"preload\" as=\"font\" href=\"\/fonts\/inter-regular.woff2\" type=\"font\/woff2\" crossorigin&gt;\n  &lt;link rel=\"preload\" as=\"font\" href=\"\/fonts\/inter-medium.woff2\" type=\"font\/woff2\" crossorigin&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">  This increases the chance the fonts arrive within the <code>optional<\/code> window for repeat visitors.<\/p>\n\n\n\n<pre class=\"mermaid\">sequenceDiagram\n    participant B as Browser\n    participant F as Font Server\n\n    Note over B: Initial navigation\n    B-&gt;&gt;B: Apply fallback font immediately\n    B-&gt;&gt;F: Request custom font (preload)\n    F--&gt;&gt;B: Font file (might arrive after 100ms window)\n    B--&gt;&gt;B: Keep fallback for this navigation&lt;br\/&gt;(font-display: optional)\n\n    Note over B: Subsequent navigation (font cached)\n    B-&gt;&gt;B: Text renders with fallback metrics&lt;br\/&gt;(Inter Fallback)\n    B-&gt;&gt;B: Swap to real font with identical metrics&lt;br\/&gt;(no CLS)<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 6: Lazy Third-Party Scripts \u2014 Tracking Without TBT<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Tag managers, analytics, chat widgets, A\/B testing tools\u2014they all love to sit on the critical path and block rendering.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two rules:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Never block rendering with third-party JS.<\/strong><\/li>\n<li><strong>Never load it for bots.<\/strong><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Loading via <code>requestIdleCallback<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Third-party scripts are scheduled only after the page is interactive:<\/p>\n\n\n\n<pre><code class=\"language-js\">if ('requestIdleCallback' in window) {\n  requestIdleCallback(loadThirdPartyScripts, { timeout: 5000 })\n} else {\n  setTimeout(loadThirdPartyScripts, 5000)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>loadThirdPartyScripts<\/code> then injects <code><script><\/code> tags for tag managers, analytics, chat, etc. They:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Never block FCP or LCP.<\/li>\n<li>Run only once the browser has spare time or after a timeout.<\/li>\n<\/ul>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    A[Page interactive] --&gt; B{requestIdleCallback&lt;br\/&gt;available?}\n    B -- Yes --&gt; C[\"Schedule loadThirdPartyScripts&lt;br\/&gt;on idle\"]\n    B -- No --&gt; D[\"setTimeout(loadThirdPartyScripts, 5000)\"]\n\n    C --&gt; E[\"Inject tag manager, analytics,&lt;br\/&gt;chat scripts\"]\n    D --&gt; E\n    E --&gt; F[\"Third-party JS runs&lt;br\/&gt;after critical rendering\"]<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Bot Detection to Skip Unnecessary JS<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For crawlers and synthetic tests, these scripts are noise. They inflate:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Total JavaScript downloaded.<\/li>\n<li>\u201cUnused JS\u201d metrics.<\/li>\n<li>TBT from script evaluation.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A multi-layer bot detection strategy avoids loading them:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Standard bot User-Agent heuristics.<\/strong><\/li>\n<li><strong>Legacy Lighthouse UAs<\/strong> containing <code>\"lighthouse\"<\/code>.<\/li>\n<li><strong>Mobile emulation quirk<\/strong>:  <\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">   Real mobile: <code>sec-ch-ua-mobile: ?1<\/code>      Lighthouse\u2019s desktop-based mobile emulation: <code>?0<\/code>      A \u201cmobile\u201d UA with <code>?0<\/code> is almost certainly synthetic.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Client-side <code>navigator.webdriver<\/code><\/strong> to catch automated browsers.<\/li>\n<\/ol>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    V[Incoming visitor] --&gt; UA[\"Check User-Agent&lt;br\/&gt;for known bots \/ #quot;lighthouse#quot;\"]\n    UA --&gt; M[\"Check sec-ch-ua-mobile&lt;br\/&gt;vs claimed device\"]\n    M --&gt; W[\"Check navigator.webdriver&lt;br\/&gt;(client-side)\"]\n    W --&gt; D{Is bot\/synthetic?}\n\n    D -- Yes --&gt; SKIP[\"Skip tag manager,&lt;br\/&gt;analytics, chat\"]\n    D -- No --&gt; LOAD[\"Allow lazy third-party&lt;br\/&gt;loading logic\"]\n\n    SKIP --&gt; R[Serve core app only]\n    LOAD --&gt; R<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When a visitor is classified as a bot or synthetic run:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Skip tag manager.<\/li>\n<li>Skip chat widgets.<\/li>\n<li>Skip analytics.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">That removes ~200 KB of JS from Lighthouse audits and keeps TBT clean without sacrificing real-user tracking.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Layer 7: Manual Chunk Splitting \u2014 Only Pay for What You Use<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nuxt\u2019s default chunks are route-based. In a CMS-driven site or large hybrid app, a catch-all page often renders dozens of section types. Without manual intervention, <strong>one giant route chunk<\/strong> ends up containing everything.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The fix is <strong>domain-based chunking<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Chunk<\/th><th>Contents<\/th><th>Loaded When<\/th><\/tr><\/thead><tbody><tr><td><code>vendor-apollo<\/code><\/td><td>Apollo Client, GraphQL libs<\/td><td>Always (core runtime)<\/td><\/tr><tr><td><code>vendor-vue-router<\/code><\/td><td>Vue Router<\/td><td>Always<\/td><\/tr><tr><td><code>cms-components<\/code><\/td><td>All CMS section components<\/td><td>When rendering CMS pages<\/td><\/tr><tr><td><code>form-components<\/code><\/td><td>Form UIs and validation libs<\/td><td>On form-heavy pages<\/td><\/tr><tr><td><code>checkout<\/code><\/td><td>Checkout flow components\/libs<\/td><td>Only in checkout<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    A[App Entry] --&gt; V1[\"vendor-apollo&lt;br\/&gt;(stable, long-cache)\"]\n    A --&gt; V2[\"vendor-vue-router&lt;br\/&gt;(stable, long-cache)\"]\n\n    A --&gt; CMS[\"cms-components&lt;br\/&gt;loaded only on CMS pages\"]\n    A --&gt; FORM[\"form-components&lt;br\/&gt;loaded on form-heavy pages\"]\n    A --&gt; CO[\"checkout&lt;br\/&gt;loaded only in checkout flow\"]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Vendor chunks are stable \u2192 long cache lifetimes.<\/li>\n<li>Application code is split by feature domain \u2192 each page type loads only the JS it actually needs.<\/li>\n<li>Chunk sizes shrink \u2192 less parse and eval time \u2192 lower TBT.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow: Finding and Fixing Bloated Chunks<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Step 1: Enable Nuxt Build Analyzer<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Configure Nuxt to generate a treemap of the client bundle:<\/p>\n\n\n\n<pre><code class=\"language-ts\">\/\/ nuxt.config.ts\nexport default defineNuxtConfig({\n  build: {\n    analyze: true,\n  },\n})<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run a production build, then open <code>.nuxt\/analyze\/client.html<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 2: Measure Code Coverage in Chrome<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open DevTools.<\/li>\n<li>Go to <strong>Coverage<\/strong> (under \u201cMore tools\u201d).<\/li>\n<li>Click <strong>Start instrumenting coverage<\/strong> and reload.<\/li>\n<li>Sort by <strong>Unused Bytes<\/strong>.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Look for large files or chunks with high unused percentages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"img\/img_3.png\" alt=\"Chrome Coverage tab showing a chunk with high unused bytes\"\/><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 3: Inspect the Chunk in Nuxt Analyze<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Open the treemap and locate the problematic chunk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"img\/img_5.png\" alt=\"Nuxt Analyze report showing all chunks and their content\"\/> <img decoding=\"async\" src=\"img\/img_6.png\" alt=\"Nuxt Analyze report showing the chunk of interest with the lottie module highlighted\"\/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hover to see which modules are inflating it\u2014often a single large dependency is responsible.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 4: Apply Targeted Optimization<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Type-only imports<\/strong><\/li>\n<\/ul>\n\n\n\n<pre><code class=\"language-ts\">  \/\/ Good\n  import type { MyType } from '@\/types'\n\n  \/\/ Bad (pulls runtime code into bundle)\n  import { MyType } from '@\/types'<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vendor chunking<\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  Explicitly place large third-party libs into dedicated vendor chunks for long-term caching.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Async loading<\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  Use <code>defineAsyncComponent<\/code> or <code>import()<\/code> for code that\u2019s only needed after specific interactions (e.g., charts, editors):<\/p>\n\n\n\n<pre><code class=\"language-ts\">  const LazyChart = defineAsyncComponent(() =&gt; import('@\/components\/Chart.vue'))<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Avoid barrel exports<\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  <code>export * from '.\/module'<\/code> can cause bundlers to pull in more than necessary. Prefer deep imports from specific files.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Server-only code isolation<\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  Put server-only logic in <code>server\/<\/code> or <code>.server.ts<\/code> files so it never leaks into client bundles.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Check global components<\/strong>  <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">  Overusing <code>global: true<\/code> generates async loading logic for every component in the main chunk, bloating it.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Nitro <code>render:html<\/code> Plugin Pattern<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Several of these techniques\u2014deferred hydration, same-origin image proxy, HTML-level tweaks\u2014use the same underlying mechanism: <strong>post-processing the final SSR HTML string<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pattern:<\/p>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    A[Nitro SSR renders HTML] --&gt; B[\"render:html hook&lt;br\/&gt;receives html.head\/body\/bodyAppend\"]\n    B --&gt; C[\"Transform HTML strings&lt;br\/&gt;(regex replace, injections)\"]\n    C --&gt; D[Return mutated HTML to Nitro]\n    D --&gt; E[Send final HTML to client]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use cases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rewriting CDN URLs to same-origin proxy paths.<\/li>\n<li>Stripping <code>modulepreload<\/code> hints before deferred hydration.<\/li>\n<li>Injecting interaction-triggered hydration scripts.<\/li>\n<li>Adding CSP nonces to inline scripts.<\/li>\n<li>Removing debug attributes in production.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">It is:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fast\u2014simple string manipulation.<\/li>\n<li>Robust\u2014no Vue internals or DOM parsing required.<\/li>\n<li>Framework-agnostic\u2014operates on the final HTML regardless of templates.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Combined Effect \u2014 From 60s to High 90s<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">No single trick yields a 97+ Lighthouse score on mobile. The gains are <strong>multiplicative<\/strong>:<\/p>\n\n\n\n<pre class=\"mermaid\">flowchart TD\n    SSR[\"SSR&lt;br\/&gt;Content visible immediately\"] --&gt; C1[\"Page Data Cache&lt;br\/&gt;Sub-ms data retrieval\"]\n    C1 --&gt; C2[\"Deferred Hydration&lt;br\/&gt;No render-blocking JS\"]\n    C2 --&gt; C3[\"Same-Origin Image Proxy&lt;br\/&gt;-594 ms LCP\"]\n    C3 --&gt; C4[\"Font Strategy&lt;br\/&gt;Zero font-induced CLS\"]\n    C4 --&gt; C5[\"Lazy Third-Party Scripts&lt;br\/&gt;-200 KB unused JS in audits\"]\n    C5 --&gt; C6[\"Manual Chunk Splitting&lt;br\/&gt;Only needed JS per page\"]\n    C6 --&gt; SCORE[\"Lighthouse 97+&lt;br\/&gt;(mobile, throttled 4G)\"]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each layer <strong>removes<\/strong> a different bottleneck:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Removed redundant data fetching (multi-tier cache).<\/li>\n<li>Removed unnecessary JS execution during the critical window (deferred hydration).<\/li>\n<li>Removed cross-origin handshakes (same-origin proxy).<\/li>\n<li>Removed layout shifts from fonts (font metrics).<\/li>\n<li>Removed third-party bloat where it doesn\u2019t matter (bots, idle loading).<\/li>\n<li>Removed dead weight from bundles (manual chunking, coverage-driven pruning).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Lessons Learned<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Targets Must Be Architectural<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Saying \u201cLCP < 2.5s\u201d is meaningless if the architecture guarantees the opposite. SSR, connection reuse, and deferral patterns are decisions you make before you write your first feature, not after Lighthouse turns red.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Biggest Wins Come From Subtraction<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The highest impact optimizations here are all deletions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Delete <code>modulepreload<\/code> hints on first load.<\/li>\n<li>Delete cross-origin connections via an image proxy.<\/li>\n<li>Delete unnecessary third-party scripts for bots.<\/li>\n<li>Delete redundant CMS or API queries with caching.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Start by asking: <em>\u201cWhat work can I remove from the critical path?\u201d<\/em> Then worry about micro-optimizing what remains.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Mobile Lighthouse Is the Only Metric That Matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Desktop numbers are easy to game into 95\u2013100. Mobile on throttled 4G is where architectural decisions are exposed. Every technique in this stack was justified against mobile numbers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Interaction-Triggered Work Is a Superpower<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Deferring hydration and other heavy tasks until the user interacts\u2014with a sane timeout fallback\u2014beats naive <code>requestIdleCallback<\/code> in many cases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The main thread stays quiet during Lighthouse\u2019s measurement window.<\/li>\n<li>Real users get instant response when they engage.<\/li>\n<li>Prefetch + yield + execute creates a smooth pipeline.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Apply this pattern to any non-critical resource: complex widgets, visualizations, content editors.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Type-Safe Form Generation from GraphQL Introspection<\/strong> \u2014 Drive form rendering and validation directly from your backend schema, eliminating duplication.<\/li>\n<li><strong>The Modular Architecture \u2014 Independent Building Blocks<\/strong> \u2014 How 35+ custom Nuxt modules keep complexity under control.<\/li>\n<li><strong>SSR Deep Dive \u2014 Hydration, State Replay, and the Cookbook<\/strong> \u2014 All the edge cases you hit when you push SSR hard in production.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Munir Husseini is a software architect specializing in full-stack TypeScript, .NET, and cloud-native architectures.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Masterclass in building a modern Nuxt 4 stack that hits Lighthouse 97\u2013100 on mobile without going \u201czero\u2011JS\u201d. Performance Is an Architecture Decision In large enterprise applications, performance rot rarely starts with \u201cslow code.\u201d It starts when the architecture cannot absorb new features without stacking more JavaScript, more requests, and more complexity on the critical path. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":234,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[5],"tags":[],"class_list":["post-19","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-advanced-web-app-with-nuxt-and-net"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score - Software Production<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/softwareproduction.eu\/?p=19\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score - Software Production\" \/>\n<meta property=\"og:description\" content=\"Masterclass in building a modern Nuxt 4 stack that hits Lighthouse 97\u2013100 on mobile without going \u201czero\u2011JS\u201d. Performance Is an Architecture Decision In large enterprise applications, performance rot rarely starts with \u201cslow code.\u201d It starts when the architecture cannot absorb new features without stacking more JavaScript, more requests, and more complexity on the critical path. [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/softwareproduction.eu\/?p=19\" \/>\n<meta property=\"og:site_name\" content=\"Software Production\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-06T21:48:40+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-07T01:22:02+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1880\" \/>\n\t<meta property=\"og:image:height\" content=\"1253\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Munir Husseini\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Munir Husseini\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19\"},\"author\":{\"name\":\"Munir Husseini\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#\\\/schema\\\/person\\\/fec48f54713e1bd117640fb9b748802f\"},\"headline\":\"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score\",\"datePublished\":\"2026-06-06T21:48:40+00:00\",\"dateModified\":\"2026-06-07T01:22:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19\"},\"wordCount\":2065,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/softwareproduction.eu\\\/wordpress\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/13-ultimate-performance-optimization.jpg\",\"articleSection\":[\"Advanced Web App with Nuxt and .NET\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19\",\"url\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19\",\"name\":\"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score - Software Production\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/softwareproduction.eu\\\/wordpress\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/13-ultimate-performance-optimization.jpg\",\"datePublished\":\"2026-06-06T21:48:40+00:00\",\"dateModified\":\"2026-06-07T01:22:02+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/softwareproduction.eu\\\/?p=19\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#primaryimage\",\"url\":\"https:\\\/\\\/softwareproduction.eu\\\/wordpress\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/13-ultimate-performance-optimization.jpg\",\"contentUrl\":\"https:\\\/\\\/softwareproduction.eu\\\/wordpress\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/13-ultimate-performance-optimization.jpg\",\"width\":1880,\"height\":1253},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/?p=19#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/softwareproduction.eu\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#website\",\"url\":\"https:\\\/\\\/softwareproduction.eu\\\/\",\"name\":\"Softwareproduction\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/softwareproduction.eu\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#organization\",\"name\":\"Munir Husseini\",\"url\":\"https:\\\/\\\/softwareproduction.eu\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/softwareproduction.eu\\\/wordpress\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/softwareproduction-logo-32.png\",\"contentUrl\":\"https:\\\/\\\/softwareproduction.eu\\\/wordpress\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/softwareproduction-logo-32.png\",\"width\":32,\"height\":32,\"caption\":\"Munir Husseini\"},\"image\":{\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/softwareproduction.eu\\\/#\\\/schema\\\/person\\\/fec48f54713e1bd117640fb9b748802f\",\"name\":\"Munir Husseini\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/b07845732d4d7bddfc43e608ae6662d564a14b35706dfae0c9610071d978f54e?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/b07845732d4d7bddfc43e608ae6662d564a14b35706dfae0c9610071d978f54e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/b07845732d4d7bddfc43e608ae6662d564a14b35706dfae0c9610071d978f54e?s=96&d=mm&r=g\",\"caption\":\"Munir Husseini\"},\"sameAs\":[\"https:\\\/\\\/softwareproduction.eu\\\/\"],\"url\":\"https:\\\/\\\/softwareproduction.eu\\\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score - Software Production","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/softwareproduction.eu\/?p=19","og_locale":"en_US","og_type":"article","og_title":"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score - Software Production","og_description":"Masterclass in building a modern Nuxt 4 stack that hits Lighthouse 97\u2013100 on mobile without going \u201czero\u2011JS\u201d. Performance Is an Architecture Decision In large enterprise applications, performance rot rarely starts with \u201cslow code.\u201d It starts when the architecture cannot absorb new features without stacking more JavaScript, more requests, and more complexity on the critical path. [&hellip;]","og_url":"https:\/\/softwareproduction.eu\/?p=19","og_site_name":"Software Production","article_published_time":"2026-06-06T21:48:40+00:00","article_modified_time":"2026-06-07T01:22:02+00:00","og_image":[{"width":1880,"height":1253,"url":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg","type":"image\/jpeg"}],"author":"Munir Husseini","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Munir Husseini","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/softwareproduction.eu\/?p=19#article","isPartOf":{"@id":"https:\/\/softwareproduction.eu\/?p=19"},"author":{"name":"Munir Husseini","@id":"https:\/\/softwareproduction.eu\/#\/schema\/person\/fec48f54713e1bd117640fb9b748802f"},"headline":"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score","datePublished":"2026-06-06T21:48:40+00:00","dateModified":"2026-06-07T01:22:02+00:00","mainEntityOfPage":{"@id":"https:\/\/softwareproduction.eu\/?p=19"},"wordCount":2065,"commentCount":0,"publisher":{"@id":"https:\/\/softwareproduction.eu\/#organization"},"image":{"@id":"https:\/\/softwareproduction.eu\/?p=19#primaryimage"},"thumbnailUrl":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg","articleSection":["Advanced Web App with Nuxt and .NET"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/softwareproduction.eu\/?p=19#respond"]}]},{"@type":"WebPage","@id":"https:\/\/softwareproduction.eu\/?p=19","url":"https:\/\/softwareproduction.eu\/?p=19","name":"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score - Software Production","isPartOf":{"@id":"https:\/\/softwareproduction.eu\/#website"},"primaryImageOfPage":{"@id":"https:\/\/softwareproduction.eu\/?p=19#primaryimage"},"image":{"@id":"https:\/\/softwareproduction.eu\/?p=19#primaryimage"},"thumbnailUrl":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg","datePublished":"2026-06-06T21:48:40+00:00","dateModified":"2026-06-07T01:22:02+00:00","breadcrumb":{"@id":"https:\/\/softwareproduction.eu\/?p=19#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/softwareproduction.eu\/?p=19"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/softwareproduction.eu\/?p=19#primaryimage","url":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg","contentUrl":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg","width":1880,"height":1253},{"@type":"BreadcrumbList","@id":"https:\/\/softwareproduction.eu\/?p=19#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/softwareproduction.eu\/"},{"@type":"ListItem","position":2,"name":"Ultimate Performance Optimization \u2014 Chasing That 100% Lighthouse Score"}]},{"@type":"WebSite","@id":"https:\/\/softwareproduction.eu\/#website","url":"https:\/\/softwareproduction.eu\/","name":"Softwareproduction","description":"","publisher":{"@id":"https:\/\/softwareproduction.eu\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/softwareproduction.eu\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/softwareproduction.eu\/#organization","name":"Munir Husseini","url":"https:\/\/softwareproduction.eu\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/softwareproduction.eu\/#\/schema\/logo\/image\/","url":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/05\/softwareproduction-logo-32.png","contentUrl":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/05\/softwareproduction-logo-32.png","width":32,"height":32,"caption":"Munir Husseini"},"image":{"@id":"https:\/\/softwareproduction.eu\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/softwareproduction.eu\/#\/schema\/person\/fec48f54713e1bd117640fb9b748802f","name":"Munir Husseini","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/b07845732d4d7bddfc43e608ae6662d564a14b35706dfae0c9610071d978f54e?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/b07845732d4d7bddfc43e608ae6662d564a14b35706dfae0c9610071d978f54e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b07845732d4d7bddfc43e608ae6662d564a14b35706dfae0c9610071d978f54e?s=96&d=mm&r=g","caption":"Munir Husseini"},"sameAs":["https:\/\/softwareproduction.eu\/"],"url":"https:\/\/softwareproduction.eu\/?author=1"}]}},"jetpack_featured_media_url":"https:\/\/softwareproduction.eu\/wordpress\/wp-content\/uploads\/2026\/06\/13-ultimate-performance-optimization.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/posts\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=19"}],"version-history":[{"count":10,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/posts\/19\/revisions"}],"predecessor-version":[{"id":235,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/posts\/19\/revisions\/235"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=\/wp\/v2\/media\/234"}],"wp:attachment":[{"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=19"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=19"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/softwareproduction.eu\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}