Why we chose Astro over SvelteKit for our hosting comparison platform
We're building HostingSift, a platform for comparing hosting providers side by side. Pricing tables, feature breakdowns, filters, user reviews. When we started, we had one decision that would shape everything else: SvelteKit or Astro?
We went with Astro. Here's why.
Some context first
HostingSift is a content-heavy site. We have 23+ hosting provider profiles, each with multiple plans, pricing tiers, feature specs, and user reviews. On top of that there are comparison pages, category listings, a recommendation quiz, and a blog.
The interactive parts are specific and well-contained: filtering plans by billing period, toggling comparison tables, authentication flows, review forms. Most of what users actually see is static content that doesn't need JavaScript to render.
This ratio matters. It shaped everything that followed.
SvelteKit was the obvious choice
We love Svelte. The reactivity model in Svelte 5 with runes is elegant. The developer experience is fantastic. SvelteKit is a mature, full-featured framework with file-based routing, SSR, API endpoints, form actions.
So why not just use it?
Because SvelteKit is an application framework. It assumes your pages are interactive by default. Every route ships a JavaScript bundle to hydrate the page client-side, even if the page is mostly static HTML.
You can work around this. You can set ssr: true and use +page.server.ts to keep data fetching on the server. You can prerender specific routes. But you're swimming against the current. The defaults push you toward shipping more JavaScript than a content site needs.
What Astro gets right for content sites
Astro starts from the opposite end. Zero client JavaScript by default. Every page renders as static HTML until you explicitly opt in to interactivity.
This is not a subtle difference. It changes how you think about building pages.
Islands, not full-page hydration
With Astro, we compose pages from .astro components (static HTML, zero JS) and Svelte components (interactive, with JS). The Svelte components are islands. They hydrate independently. The rest of the page is just HTML that the browser renders immediately.
Our hosting profile page is a good example. The page layout, provider description, specs table, pros and cons list, SEO metadata... all static Astro components. Zero JavaScript shipped. Then we drop in a PlanCard component with client:load for the interactive pricing table where users toggle billing periods. A ReviewForm for authenticated review submission. A HeaderAuth for login state.
---
import Layout from '../../layouts/Layout.astro'
import PlanCard from '../../components/PlanCard.svelte'
import ReviewSection from '../../components/ReviewSection.svelte'
import SpecsTable from '../../components/SpecsTable.astro'
const { hosting, plans } = await fetchHostingData(slug)
---
<Layout title={hosting.name}>
<article>
<h1>{hosting.name}</h1>
<p>{hosting.description}</p>
<!-- Static HTML, no JS overhead -->
<SpecsTable specs={hosting.specs} />
<!-- Interactive islands, hydrate independently -->
<PlanCard client:load plans={plans} />
<ReviewSection client:load hostingId={hosting.id} />
</article>
</Layout>
Each island hydrates on its own. If one fails, the rest of the page still works fine. In SvelteKit, the entire page is a single hydration unit.
Prerendering is the default, not an opt-in
In Astro's hybrid mode, pages are prerendered at build time unless you explicitly say otherwise. Our homepage, category pages, legal pages, blog posts... all static HTML files. Fast, cacheable, no server compute on each request.
For pages that need fresh data (hosting profiles with live pricing, filtered listings), we mark them as server-rendered. It's explicit and intentional.
SvelteKit takes the opposite approach. Everything is server-rendered by default. You opt in to prerendering per route. The mental model is reversed. For a site where the majority of pages can safely be static, Astro's default aligns better with reality.
We still write Svelte
This is what made the decision easy. Astro is not a replacement for Svelte. It's a composition layer around it.
All our interactive components are pure Svelte 5 with runes, stores, reactive state. We didn't lose anything from the Svelte ecosystem. We just moved the routing and page composition up to Astro, where it can make smarter decisions about what ships to the browser.
The pattern feels natural once you get used to it. Page structure lives in .astro files (templating with zero overhead). Interactive behavior lives in .svelte files (full reactivity where it's actually needed).
The tradeoffs we accepted
Astro is not a free lunch. Here's what we gave up.
No form actions
SvelteKit's form actions with progressive enhancement are genuinely nice. In Astro, we handle forms through our Hono API with client-side fetch calls. It works, but there's more boilerplate involved. We write onSubmit handlers and manage loading states manually where SvelteKit would handle the plumbing for us.
Two component models
Anyone working on the codebase needs to understand both .astro and .svelte files. When does something belong in Astro vs Svelte? The rule is straightforward (static content = Astro, interactive behavior = Svelte), but it's still a decision you make dozens of times a day.
View Transitions have rough edges
Astro's View Transitions look great but come with real gotchas. Inline scripts re-execute on each navigation. We spent more time than expected getting Google Analytics and vanilla-cookieconsent to behave correctly, adding guard flags to prevent double initialization. SvelteKit's client-side routing handles this more gracefully out of the box.
Separate API server
SvelteKit has +server.ts for API routes, tightly integrated with the framework. We chose to run a standalone Hono server for our API. This was intentional (the API will eventually serve a mobile app too), but it means two processes to manage, separate deployments, and CORS configuration that SvelteKit wouldn't need.
Where this paid off
The results justified the decision.
Our homepage ships about 40KB of JavaScript total. The interactive bits (provider grid, newsletter form) are small isolated bundles. In a SvelteKit setup, the framework's client-side router adds baseline overhead before any of our code even loads.
Server-side OG image generation fits naturally into Astro's endpoint model. We generate dynamic Open Graph images for every hosting profile, comparison page, and blog post using Satori and Sharp. It's just a standard API route that returns a PNG buffer. No special configuration needed.
Build times stay fast. Prerendering 50+ static pages, generating sitemaps, and compiling the Svelte islands takes under 30 seconds. Adding a new provider doesn't slow anything down because the build is mostly parallel.
The deployment question
This one doesn't get discussed enough.
SvelteKit works everywhere in theory. In practice, the smoothest path is deploying to Vercel. Rich Harris works there, adapter-auto detects Vercel out of the box, and the integration is polished. You can absolutely use adapter-node and self-host, but you'll be configuring things that Vercel handles for you automatically.
We wanted to self-host. Our stack runs on a single Hetzner ARM64 VPS: the Astro site, the Hono API, PostgreSQL, all behind nginx and Cloudflare. The whole thing costs about €5 a month.
With Astro, deploying to a VPS was straightforward. Install @astrojs/node, build, run with PM2. That's it. No adapter quirks, no platform-specific edge cases. The output is a standard Node.js server.
The cost angle is worth mentioning too. Platforms like Vercel and Netlify use usage-based pricing. The Pro plans start at $20-25 per month, but the real cost depends on traffic, serverless function execution time, and bandwidth. Overages are billed automatically. There are well-documented cases of bills spiking unexpectedly after traffic surges or bot crawls. For a content site that serves a lot of static pages, this pricing model works against you.
A fixed-cost VPS eliminates that variable entirely. Traffic can double or triple and the bill stays the same. If we outgrow the current server, the next tier up is still a fraction of what a managed platform would cost at equivalent traffic. We're not locked into any vendor's pricing model, and migrating to a bigger box is a straightforward process.
To be fair, managed platforms give you CI/CD, preview deployments, edge functions, and zero-config scaling. Those are real features with real value, especially for teams that don't want to manage infrastructure. The tradeoff is control and cost predictability versus convenience.
For us, a €5 VPS with full control was the obvious pick.
After several months in production
23 hosting providers, hundreds of plans, comparison pages, a quiz, a blog, user auth, admin panel. All running on that single Hetzner box.
The architecture has held up. New provider profiles are just data. New interactive features are new Svelte islands dropped into existing pages. New static pages are .astro files with no client-side cost.
Would SvelteKit have worked? Of course. For a more app-like product with dashboards, real-time collaboration, heavy client state, we'd pick SvelteKit without thinking twice.
But for a content-first site where interactivity is the exception rather than the rule, Astro with Svelte islands turned out to be the right call. Not because SvelteKit couldn't do it. Because Astro's defaults already pointed in the direction we wanted to go.
There's real value in not having to fight your framework.
Top comments (0)