Optimizing My Hugo Blog: Journey to Zero JS
TL;DR
I reduced my Hugo blog’s page weight by eliminating 3.6 MB of JavaScript and 40 KB of external CSS, achieving a 100% JS-free frontend. Key optimizations included HTML minification, inlining CSS, switching to native MathML, and pre-rendering Mermaid diagrams server-side.
I recently looked into my blog’s performance and was surprised to find my pages were downloading over 3.6 MB of JavaScript and render-blocking CSS on every load. For a simple static site, this was too much, so I decided to optimize it.
Here is the step-by-step breakdown of how I reduced my payload and removed JavaScript.
The Baseline
Before starting, my site had some major issues:
- HTML Size: 86,348 bytes
- JS Size: 3,617,515 bytes (3.6 MB)
- CSS Size: 40,560 bytes
- Issue: Massive blocking JS/CSS scripts were loaded on every single page for Mermaid diagrams and Math rendering. The HTML was also not minified.

Optimization 1: HTML Minification
The first step was simple: adding minifyOutput = true to hugo.toml.
- HTML Size: 72,370 bytes (16% smaller)
- Impact: Reduced parsing time for HTML, leading to a faster First Paint.

Optimization 2: Inlining CSS
Next, I removed the <link> tag pointing to my main.css file and replaced it with an inline <style>{{.Content|safeCSS}}</style> block.
- HTML Size: Increased to 127,350 bytes (because CSS is now inside the HTML document).
- Impact: This eliminated 1 critical render-blocking HTTP request. The browser no longer waits for an external CSS fetch, which improves First Contentful Paint (FCP).

Optimization 3: Native MathML
My blog used the KaTeX library (JS, CSS, and fonts) to render equations. I removed it and enabled Hugo’s Goldmark passthrough extensions to render Native MathML instead.
- HTML Size: 123,341 bytes
- JS Size: 3,338,725 bytes (278 KB smaller)
- CSS Size: 0 bytes (Removed KaTeX CSS, meaning zero external stylesheets are loaded).
- Impact: A significant reduction in payload size. I removed the need for JavaScript and font files for math. The browser now renders it natively.

Optimization 4: Conditional Asset Loading
My Mermaid script was loading on every page. I used Hugo’s .Store to set a flag hasMermaid when processing Markdown, and only injected the Mermaid <script> tag if that flag is true.
- HTML Size: 117,632 bytes (Saved 6 KB across all generated pages).
- Impact: Text-only blog posts no longer force the browser to download
mermaid.min.js. The JavaScript is only loaded when necessary.
(Text-only pages don’t load Mermaid)
(Pages with diagrams load Mermaid conditionally)
Optimization 5: Server-side Rendering for Mermaid Diagrams
Even conditionally, loading a 3.3 MB Mermaid script on some pages was heavy. I introduced a Node.js build step to pre-render Mermaid blocks into static SVG files. Now, the frontend outputs an <img src="diagram.svg">.
- JS Size: 0 bytes (Removed the remaining 3.3 MB of Mermaid JavaScript).
- Impact: The site is now 100% JavaScript-free on the frontend. The
Total Blocking Time (TBT)metrics improved because the browser no longer executes JS to calculate layouts.

Optimization 6: Early Hints & Caching
Finally, I optimized the network layer. I generated a _headers file to define strict Cache-Control rules for immutable assets. I also added Link: <image>; rel=preload; as=image directives automatically via the build script.
- Impact: Cloudflare will now return
103 Early Hintsresponses, telling the browser to fetch SVGs and images immediately. Even before the HTML document finishes downloading. Assets cache indefinitely on repeat visits, eliminating secondary network fetch delays.
Final Summary
Over the course of these 6 optimizations, I successfully brought the frontend static vendor sizes from:
- JS Payload: 3.6 MB -> 0 bytes (100% reduction)
- External CSS: 40 KB -> 0 bytes (Eliminated all external style sheets, saving a round-trip on every page).
- HTML Payload: Minified by 16% initially, offset slightly by securely inlining CSS, ensuring near-instantaneous
First Contentful Paint.
Performance matters, and sometimes you don’t need a heavy JS framework to deliver a fast experience!