Svelte Introduction — Essentials for Inertia × Laravel
A beginner’s guide to using Svelte with Laravel and Inertia.js. Covers Svelte 5 runes, template syntax, useForm, usePage, shadcn-svelte, and the Laravel starter kit setup.
Svelte occupies a unique position among JavaScript frameworks. While React and Vue operate as runtime libraries, Svelte works as a compiler. At build time it transforms your components into plain JavaScript — no framework runtime ships to the browser.The defining feature of Svelte is that it doesn’t use a Virtual DOM. When state changes, code generated by the Svelte compiler updates the DOM directly. The result is extremely lean, fast UIs.
This page covers Svelte 5 paired with Inertia v3. Laravel 13 starter kits use this combination by default.
Svelte 5 (released 2024) introduced a new reactivity system called Runes — special functions like $state, $derived, and $effect that declare reactive state explicitly. Unlike Svelte 4’s implicit reactivity, runes are predictable and easy to reason about.
<script lang="ts"> let count = $state(0) function increment() { count++ }</script><button onclick={increment}>{count}</button>
The Laravel Svelte starter kit uses Svelte 5 + TypeScript as the standard. All examples on this page follow that convention.
Svelte’s relationship with Laravel is the newest of the three frontend options — the official starter kit was the first formal support.With Laravel 13 (2026) adding Svelte to the starter kits, Svelte became an official frontend choice in the Laravel ecosystem — on equal footing with Vue and React, selectable from the laravel new interactive prompt.Svelte is still largely unknown to Laravel developers, but its compiler-powered lightweight bundles and concise syntax mean you’ll write noticeably less code compared to Vue or React.
The mainstream way to use Svelte with Laravel today is Inertia × Svelte. Inertia lets you pass data directly from a Laravel controller to a Svelte component — no REST API required. This “modern monolith” architecture delivers SPA-like UX without a separate API layer.
To add Svelte to an existing project, install the server-side and client-side packages separately.
# Server side (PHP)composer require inertiajs/inertia-laravel# Client side (JavaScript)npm install @inertiajs/svelte @inertiajs/vite sveltenpm install --save-dev @sveltejs/vite-plugin-svelte svelte-check typescript
Add the Svelte plugin and the Inertia Vite plugin to vite.config.ts:
import { defineConfig } from 'vite'import laravel from 'laravel-vite-plugin'import { svelte } from '@sveltejs/vite-plugin-svelte'import inertia from '@inertiajs/vite'export default defineConfig({ plugins: [ laravel({ input: ['resources/css/app.css', 'resources/js/app.ts'], refresh: true, }), svelte(), inertia(), ],})
Bootstrap the Inertia app in resources/js/app.ts. The @inertiajs/vite plugin handles page resolution and mounting automatically, so a minimal entry point is all you need.
import { createInertiaApp } from '@inertiajs/svelte'createInertiaApp()
For the full manual setup (root template, middleware registration) see the Inertia documentation.
A .svelte file consists of three blocks: <script>, template, and <style>.
<script lang="ts"> // Logic (TypeScript) let name = $state('Laravel')</script><!-- Template (HTML-like syntax) --><h1>Hello, {name}!</h1><style> /* Scoped CSS — applies only to this component */ h1 { color: #ff2d20; }</style>
The structure resembles Vue’s Single File Component (SFC), but with even less boilerplate. <style> is scoped by default, so class name collisions are never a concern.
<script lang="ts"> let isLoggedIn = $state(false) let role = $state('editor')</script>{#if isLoggedIn} <p>Welcome back!</p>{:else if role === 'admin'} <p>Logged in as administrator</p>{:else} <a href="/login">Log in</a>{/if}
Equivalent to Vue’s v-if / v-else or React’s ternary expressions.
bind:value synchronises a form element with a reactive variable in both directions.
<script lang="ts"> let title = $state('') let agreed = $state(false) let role = $state('viewer')</script><!-- Text input --><input bind:value={title} type="text" /><p>Typing: {title}</p><!-- Checkbox --><input bind:checked={agreed} type="checkbox" /><p>Agreed: {agreed}</p><!-- Select --><select bind:value={role}> <option value="viewer">Viewer</option> <option value="editor">Editor</option> <option value="admin">Admin</option></select>
Equivalent to Vue’s v-model. In React you would need an onChange handler for every field — Svelte’s bind: replaces all of that with a single declarative attribute.
The <Link> component from @inertiajs/svelte performs page transitions over XHR, avoiding full browser reloads.
<script lang="ts"> import { Link } from '@inertiajs/svelte'</script><!-- Basic link --><Link href="/posts">All posts</Link><!-- Link that sends a DELETE request --><Link href="/posts/1" method="delete" as="button"> Delete</Link><!-- Prefetch on hover --><Link href="/posts/1" preload>View post</Link>
It looks like a plain <a> tag, but Inertia swaps only the page component underneath — giving you SPA-like navigation without a full reload.
The <Form> component from @inertiajs/svelte is the recommended form-submission style used in the starter kit’s authentication pages. You specify action and method as props and write the form body using a {#snippet}.
{#snippet children({ errors, processing })} is Svelte’s snippet syntax — the content block passed into a component (equivalent to Vue slots or React render props). errors and processing are computed and injected automatically by the Form component. Form fields use the native name attribute instead of bind:value, so the browser’s standard form data collection handles serialisation.
The starter kit uses Wayfinder to manage routes as objects. store.form() returns an object containing action and method which is spread onto <Form>.
<script lang="ts"> import { Form } from '@inertiajs/svelte' import { store } from '@/routes/login'</script><Form {...store.form()} resetOnSuccess={['password']} class="flex flex-col gap-6"> {#snippet children({ errors, processing })} <!-- form body --> {/snippet}</Form>
resetOnSuccess lists fields to clear after a successful submission — useful for password fields that should be emptied after the form is sent.
Without Wayfinder, passing the URL directly (action="/login") works just the same.
When validation errors are returned, useForm preserves the entered data and displays the errors. Combined with bind:value, this produces a seamless form experience.
<script lang="ts"> let count = $state(0) let isOpen = $state(false) let items = $state<string[]>([])</script><p>{count}</p><button onclick={() => count++}>+1</button><button onclick={() => isOpen = !isOpen}>Toggle</button>
Variables declared with $state are automatically reactive. When the value changes the DOM updates automatically. No .value property to access — just assign directly.
$effect runs whenever the $state variables it reads change. Unlike React’s useEffect, there is no dependency array — Svelte tracks accessed $state variables automatically.
The starter kit includes shadcn-svelte, a Svelte port of the shadcn/ui component library. Components are copied into your project so you can customise them freely.
Svelte pairs naturally with Laravel through Inertia, especially in the “modern monolith” setup. Its compiler-based design keeps bundle sizes small, and runes make reactivity explicit and easy to follow.
Piece
Role
Laravel controller
Routing, data retrieval, validation
Inertia::render()
Pass data from the controller to a Svelte component
Svelte page component
Receive props with $props() and render the UI
useForm
Form state management, submission, error display
Link component
Client-side navigation without full reloads
usePage().props
Access shared data from any component
shadcn-svelte
Standard component library
With Inertia × Svelte you get the productivity of Laravel’s backend combined with Svelte’s compact, compiler-optimised frontend. Run laravel new and pick Svelte to get authentication pages, TypeScript, Tailwind, and shadcn-svelte out of the box.
Inertia.js Documentation
See the official Inertia v3 docs for the full feature reference.