[{"data":1,"prerenderedAt":231},["ShallowReactive",2],{"post-\u002Fblog\u002F2026-04-why-we-ship-static":3},{"id":4,"title":5,"author":6,"body":7,"description":217,"draft":218,"extension":219,"meta":220,"navigation":221,"ogImage":222,"path":223,"publishedAt":224,"seo":225,"stem":226,"tags":227,"updatedAt":222,"__hash__":230},"blog\u002Fblog\u002F2026-04-why-we-ship-static.md","Why we ship static","callum-peers",{"type":8,"value":9,"toc":202},"minimark",[10,14,17,22,44,51,55,60,63,71,75,78,81,85,88,91,94,98,101,128,132,135,155,159,172,179,183,186,189,192],[11,12,13],"p",{},"The Vantara Labs company site is, by design, a pile of prerendered HTML files on a CDN. No server rendering, no edge functions for anything reader-facing, no database. One Cloud Function exists for contact-form submissions, and that is the full dynamic surface.",[11,15,16],{},"That sounds austere, and in 2026 it probably reads as a little old-fashioned. A lot of the tooling ecosystem has moved towards server-rendered, edge-first, \"resumable\" frameworks that do impressive things. We looked at that end of the dial and chose the quiet end on purpose. Here is the reasoning.",[18,19,21],"h2",{"id":20},"what-ship-static-actually-means-for-us","What \"ship static\" actually means for us",[11,23,24,25,35,36,39,40,43],{},"Concretely: every page on this site is generated at build time by ",[26,27,31],"a",{"href":28,"rel":29},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fapi\u002Fcommands\u002Fgenerate",[30],"nofollow",[32,33,34],"code",{},"nuxt generate",", uploaded to Firebase Hosting, and served from the nearest edge PoP to the reader. When you hit ",[32,37,38],{},"vantaralabs.co.uk\u002Fabout",", you are getting a prebuilt ",[32,41,42],{},"about\u002Findex.html",", not calling a function.",[11,45,46,47,50],{},"We keep one tiny dynamic bit, the ",[32,48,49],{},"\u002Fapi\u002Fcontact"," Cloud Function, but that is gated behind a form submission, not on every pageview. The blog, the product pages, the RSS feed, the sitemap, the Open Graph images: all built once, served many times.",[18,52,54],{"id":53},"why-in-order-of-importance","Why, in order of importance",[56,57,59],"h3",{"id":58},"_1-the-site-is-read-far-more-often-than-it-is-written","1. The site is read far more often than it is written",[11,61,62],{},"We will update this site a few times a week at most. It is read thousands of times between updates. Rebuilding on every request (even with caching in front) is paying for something the reader never benefits from.",[11,64,65,66,70],{},"Build-time rendering inverts the cost: we pay once, during the push, and every subsequent reader gets a file off a CDN. The only time a reader waits for compute is when the ",[67,68,69],"em",{},"author"," did something new.",[56,72,74],{"id":73},"_2-the-failure-modes-are-smaller","2. The failure modes are smaller",[11,76,77],{},"When a static site breaks, it breaks during CI. Our worst-case deployment failure is \"the build fails and the previous version stays live\", which is a normal day. We cannot push a regression that only shows up for one in a thousand visitors because our infrastructure doesn't have thousand-visitor variance. Every reader gets the same HTML.",[11,79,80],{},"No database means no database outage. No server runtime means no runtime upgrade breaking a library at 03:00. No regional failover complexity because CDN PoPs do that for us.",[56,82,84],{"id":83},"_3-it-costs-approximately-nothing","3. It costs approximately nothing",[11,86,87],{},"Firebase Hosting's free tier serves 10GB of egress per month before any cost kicks in. Our site is a few hundred KB of HTML, images, and fonts per pageview. Back-of-envelope: we would need to serve roughly 50,000 pageviews a month before we pay a penny.",[11,89,90],{},"The Cloud Function for the contact form is the only thing metered on invocations, and that is a handful of form submissions a day at most.",[11,92,93],{},"A small studio's website budget should be \"the domain renewal and nothing else\" until we are at a size where that assumption breaks.",[56,95,97],{"id":96},"_4-determinism-makes-compliance-boring","4. Determinism makes compliance boring",[11,99,100],{},"UK GDPR, the Companies Act 2006 disclosure requirement, ICO guidance on cookies: all easier to comply with on a static site, because:",[102,103,104,112,118],"ul",{},[105,106,107,111],"li",{},[108,109,110],"strong",{},"There is no server-side personal data processing."," We know this because there is no server-side anything on page loads.",[105,113,114,117],{},[108,115,116],{},"Consent Mode v2 can default to \"denied\" without the analytics ever having loaded",", because no third-party script runs until the reader opts in. Which is the whole point of Consent Mode v2.",[105,119,120,123,124,127],{},[108,121,122],{},"Auditing is literally reading the files we ship."," The HTML shipped to readers is identical to the HTML in the repo's ",[32,125,126],{},".output\u002Fpublic"," folder. No hidden runtime branch.",[18,129,131],{"id":130},"what-we-give-up","What we give up",[11,133,134],{},"We are not pretending the trade-off is free. The things we cannot do on a static site:",[102,136,137,143,149],{},[105,138,139,142],{},[108,140,141],{},"Personalisation on pageload."," The same HTML goes to everyone. If we needed per-reader content (e.g. a logged-in dashboard), the products would need server rendering. They do, and they live on their own domains with their own frameworks.",[105,144,145,148],{},[108,146,147],{},"True real-time content."," If something breaks into the news and we want a blog post up in 90 seconds, we wait on a build. For a company site, this is a feature, not a bug.",[105,150,151,154],{},[108,152,153],{},"Runtime A\u002FB testing."," We do not need it yet. When we do, we will reach for static edge rewrites before anything heavier.",[18,156,158],{"id":157},"where-static-stops-being-the-answer","Where static stops being the answer",[11,160,161,162,166,167,171],{},"For the portfolio's actual products, ",[26,163,165],{"href":164},"\u002Fproducts\u002Fpelosi-tracker","Pelosi Tracker"," and ",[26,168,170],{"href":169},"\u002Fproducts\u002Fleyden","Leyden",", static-only would be wrong. Those products serve live-updating data, process payments, gate features behind auth. They need server runtime and we give them one.",[11,173,174,175,178],{},"But the ",[67,176,177],{},"company website"," is a brochure and a blog. We would have to invent requirements to justify running a server for it.",[18,180,182],{"id":181},"the-boring-recommendation","The boring recommendation",[11,184,185],{},"Most small-studio and freelance websites are over-engineered. Nuxt, Next, Astro, SvelteKit all have \"generate static\" as a first-class target. Pick your favourite framework, generate the site, host it on Cloudflare Pages, Netlify, Firebase Hosting, or GitHub Pages, and go build the product.",[11,187,188],{},"The exciting frameworks will still be there when you actually need them.",[190,191],"hr",{},[11,193,194],{},[67,195,196,197,201],{},"Part of a short series on how the Vantara Labs site is put together. Next one: how we manage the GCP side of things with Terraform. Subscribe via the ",[26,198,200],{"href":199},"\u002Fblog\u002Frss.xml","RSS feed"," if that is your thing.",{"title":203,"searchDepth":204,"depth":204,"links":205},"",2,[206,207,214,215,216],{"id":20,"depth":204,"text":21},{"id":53,"depth":204,"text":54,"children":208},[209,211,212,213],{"id":58,"depth":210,"text":59},3,{"id":73,"depth":210,"text":74},{"id":83,"depth":210,"text":84},{"id":96,"depth":210,"text":97},{"id":130,"depth":204,"text":131},{"id":157,"depth":204,"text":158},{"id":181,"depth":204,"text":182},"This site is prerendered HTML on a CDN. Here is the reasoning, and why we think most small-studio websites should be the same.",false,"md",{},true,null,"\u002Fblog\u002F2026-04-why-we-ship-static","2026-04-17",{"title":5,"description":217},"blog\u002F2026-04-why-we-ship-static",[228,229],"engineering","infrastructure","MoK7sWXNzCrkiRRFBe2ilYWb_x7PgG9gy71jf383J5I",1776592126986]