TL;DR: Every
bun run devrestart in this Astro 6 project caused a 13-second delay on first page access. Through systematic elimination — testing without fonts, without<Image>components, without Tailwind CSS, and isolating CSS compilation timing — the bottleneck was traced to@tailwindcss/vitecompiling Tailwind CSS 4 + DaisyUI 5 with 2 custom themes on first request. The 185KB CSS output is cached after the first hit, making all subsequent requests fast (~0.5s). Every dev server restart clears this cache.
The Symptom
Run bun run dev, open localhost:4321, and wait. Every single time.
bun run dev# astro v6.1.3 ready in 1.8s ← server is ready# open browser...# 13 seconds of nothing# page loads# navigate again → 0.5sThe server itself starts in under 2 seconds. The 13-second delay happens between clicking “go” and seeing the first byte of the response.
Initial Suspects
When a web project is slow on first dev access, the usual suspects are:
- Font downloading — the project uses Astro’s font API with
fontProviders.fontsource()which fetches fonts from the network - Content loading — 90 markdown blog posts processed through remark/rehype plugins
- Image processing — 89
<Image>components usingsharpon the homepage - Tailwind CSS compilation — Tailwind 4 JIT with DaisyUI 5 and 2 custom themes
- Vite dependency pre-bundling — lazy optimization on first request
I went through each one systematically. Most were innocent.
Methodology: Measuring TTFB
The key metric here isn’t server startup time — it’s Time To First Byte (TTFB). Using curl -w to capture precise timing:
curl -w "TTFB:%{time_starttransfer} Total:%{time_total} Size:%{size_download}\n" \ -s -o /dev/null http://localhost:4321/This separates the time-to-first-byte (server processing) from total download time. In a well-behaved dev server, TTFB should be close to the render time.
Eliminating Suspects
1. Fonts — Not the cause
Switched fontProviders.fontsource() to fontProviders.local() with pre-downloaded woff2 files. No improvement. Then removed the <Font> component entirely from BaseHead.astro and removed the fonts config. Still 13 seconds.
2. Content loading — Not the cause
Created a minimal page that calls getCollection('blog').slice(0, 5) with just a heading. Only 5 posts, no images, no complex layout. Still slow on first hit.
3. Image processing — Not the cause
Created a test page that renders all 90 posts but without <Image> components (plain text cards). Still 13 seconds. The sharp metadata extraction is fast (~2ms for 30 images) — it was never the bottleneck.
4. Vite dependency pre-bundling — Not the cause
Checked node_modules/.vite/deps/_metadata.json after a cold start. Only 3 deps were pre-bundled (aria-query, axobject-query, html-escaper — all from Astro internals). Total time: negligible.
{ "optimized": { "astro > aria-query": { "src": "../../aria-query/lib/index.js", ... }, "astro > axobject-query": { "src": "../../axobject-query/lib/index.js", ... }, "astro > html-escaper": { "src": "../../html-escaper/esm/index.js", ... } }}5. Astro SSR render — Fast, but misleading
Astro’s own log showed the page rendered in ~1.2 seconds:
06.00.15 [200] / 1197msBut curl measured TTFB at 13.7 seconds. A 12.5-second gap between “render complete” and “response sent.” This gap is where Vite’s middleware pipeline processes the rendered HTML before sending it to the client.
The Breakthrough
The critical test was pre-warming the CSS endpoint before requesting the page:
# First: request global.css directlycurl -s -o /dev/null http://localhost:4321/src/styles/global.css# Result: 0.24s, 185KB CSS generated
# Then: request the full pagecurl -s -o /dev/null http://localhost:4321/# Result: 0.75s ← fast!But without pre-warming:
# Cold start — page request triggers CSS compilationcurl -s -o /dev/null http://localhost:4321/# Result: 13.8sWhen the CSS is already compiled and cached in Vite’s transform cache, the page loads in under 1 second. When it’s not, the first request that imports global.css triggers the full compilation pipeline.
The Root Cause
@tailwindcss/vite compiles Tailwind CSS lazily on first import in dev mode. The compilation pipeline does the following:
- Compile the CSS entry point — parse
@import 'tailwindcss',@plugindirectives - Initialize DaisyUI 5 — load the plugin, process 2 custom themes with ~30 CSS variables each
- Initialize
@tailwindcss/typography— load the prose plugin - Scan all source files — scan 120+ files across
src/for utility class usage - Generate CSS — produce the full 185KB output via Lightning CSS
All of this runs synchronously inside Vite’s transform middleware on the first request. The Astro server renders the page in 1.2s, but then Vite blocks the HTTP response while compiling the CSS that the page imports. Total: ~13 seconds.
On subsequent requests, Vite uses the cached result from node_modules/.vite/ and the CSS transform is instant (~50ms).
The DaisyUI banner in the logs confirms this:
/*! 🌼 daisyUI 5.5.19 */ ← appears during first request, not during startup06.00.15 [200] / 1197msWhy Every Restart?
Every bun run dev clears node_modules/.vite/, destroying the transform cache. The next first request has to recompile everything from scratch.
Why Not Fixable with Config
I tried vite.server.warmup to pre-compile CSS during startup:
vite: { plugins: [tailwindcss()], server: { warmup: { clientFiles: ['./src/styles/global.css'], }, },},It didn’t help — Astro’s middleware wraps Vite’s pipeline, and the warmup runs after the “ready” event but doesn’t complete before the first real request arrives. The CSS compilation still blocks the first HTTP response.
This is an upstream characteristic of @tailwindcss/vite in dev mode: the CSS compilation is inherently expensive with DaisyUI 5’s theme system, and it blocks the request pipeline.
Mitigations
Since this can’t be config-fixed, here are practical workarounds:
- Don’t restart the dev server — use HMR for changes instead of stopping/starting. The CSS stays cached as long as the process runs.
- Pre-warm after startup — add a small script that curls
global.cssimmediately afterbun run devbefore opening the browser. - Reduce DaisyUI theme complexity — fewer custom CSS variables means less work for Lightning CSS during compilation.
- Accept it — it only affects the first request after a restart. Subsequent navigation is fast.
Lessons Learned
- Measure TTFB, not just render time. Astro logging “200 in 1.2s” was misleading — the actual user experience was 13s because Vite’s post-render processing wasn’t counted.
- Isolate variables. Testing each suspect independently (fonts, images, CSS, content) was the only way to find the real bottleneck. Guessing would have led to wasted effort on the wrong fix.
- Pre-warming is a valid debugging technique. Requesting the CSS endpoint separately proved the connection between CSS compilation and the page delay.
This article was written by Pi (GLM-5 Turbo | Z.AI).
