QUANHEX.
Architecture

From SvelteKit to Inertia.js: Why We Changed the Architecture

We started a SaaS project in SvelteKit with a separate Laravel API. Six weeks in, we switched to Inertia.js. Here's what drove that decision and what we learned.

· 6 min read

Starting a new SaaS project involves a hundred small architectural decisions that compound over time. The frontend-backend split is one of the biggest. We made the wrong call initially and corrected it early enough to avoid serious debt. Here’s the full story.

The Original Setup

Week 1: A SvelteKit application as the frontend, a Laravel API as the backend, communicating via a JSON API. This is a well-established pattern. Many successful applications are built this way.

What we underestimated: the overhead of maintaining the API contract as a small team.

Six Weeks of API Contract Friction

Every new feature required two implementations: the API endpoint in Laravel and the data fetching in SvelteKit. Route handling existed in two places. Type definitions for API responses had to be maintained separately from the PHP classes that produced them.

Simple things accumulated: authentication required Sanctum token management on the frontend, CORS configuration to keep updating as we added endpoints, and a growing collection of SvelteKit load functions that were essentially just fetching from our own API.

The development loop was: write PHP controller → write PHP route → write TypeScript types → write SvelteKit load function → write SvelteKit component. Five steps for one feature.

The Switch to Inertia.js

Week 7: We replaced SvelteKit with Svelte (standalone, compiled by Vite) and added Inertia.js as the glue between Laravel and Svelte.

The development loop became: write PHP controller → write Svelte component. Two steps for one feature.

Authentication is handled by Laravel’s session-based auth. No tokens, no CORS. The controller passes data as props. The Svelte component renders them. Navigation is handled by Inertia’s client-side routing.

// Controller
return Inertia::render('Reservations/Index', [
    'reservations' => ReservationResource::collection($reservations),
    'filters' => $request->only(['status', 'date']),
]);
<script>
  let { reservations, filters } = $props();
</script>

There’s no API. There’s no fetch call. There’s no CORS. There’s no token management.

What We Gave Up

SvelteKit’s file-system routing is excellent. Going back to Inertia means Laravel owns routing — which is fine, but it means one mental model instead of two, and we’re keeping the Laravel one.

SvelteKit’s SSR is more sophisticated than Inertia’s server-side rendering. For a product behind authentication, this doesn’t matter — SSR for authenticated pages provides minimal SEO benefit. For a marketing site, SvelteKit or Astro is still the right choice.

The public-facing marketing site (the Quanhex.com site you’re reading this on) is Astro. The authenticated SaaS application is Laravel + Inertia + Svelte. Each tool in its appropriate context.

The Broader Lesson

Architecture decisions have switching costs. The right time to reconsider an architectural choice is as early as possible after you have enough evidence that it’s wrong.

Six weeks of friction was enough evidence. The switch took four days. If we’d waited six months, it would have taken four weeks.

Build the simplest architecture that solves the problem. Add complexity only when you have a concrete reason — not because a pattern is fashionable or because you might need the flexibility someday.

For solo SaaS development, the “separate frontend and backend” pattern adds more overhead than it’s worth until you have a team large enough to split ownership of each half.