Getting started with Svelte — the essentials for using it with Inertia × Laravel
An introductory guide to using Svelte with Laravel + Inertia.js. Covers the compiler-based approach and Svelte 5 runes syntax, plus practical usage of Inertia Svelte hooks like useForm and usePage.
Svelte occupies a unique position among JavaScript frameworks. While React and Vue work as runtime libraries, Svelte works as a compiler. It converts components to pure JavaScript at build time, so no extra framework code needs to be shipped to the browser.Svelte’s biggest characteristic is that it doesn’t use a Virtual DOM. When state changes, code Svelte generated at compile time directly updates the DOM. This yields extremely lightweight and fast UIs.
This page describes the combination of Svelte 5 and Inertia v3. The Laravel 13 starter kit uses this stack by default.
Svelte 5 (released 2024) introduced a new reactivity system called Runes. You declare reactive state using special functions (runes) like $state, $derived, and $effect. Unlike the implicit reactivity of Svelte 4 and earlier, the design is explicit and predictable.
<script lang="ts"> let count = $state(0) function increment() { count++ }</script><button onclick={increment}>{count}</button>
Laravel’s Svelte starter kit uses Svelte 5 + TypeScript as the standard. All examples on this page are written in TypeScript (lang="ts").
Svelte’s relationship with Laravel is much newer than Vue’s or React’s; the first official support is starter kit adoption.With Laravel 13 in 2026, Svelte was officially added to the starter kits, making Svelte an official frontend option in the Laravel ecosystem. It’s treated as equal to Vue and React and is selectable in the laravel new interactive prompt.Svelte is still relatively unknown to many Laravel users, but its compiler-driven small bundles and simple syntax stand out—users switching from Vue or React are often surprised by how little they need to write.
The center of gravity for Svelte in Laravel today is Inertia × Svelte. Inertia enables a “modern monolith” architecture where data flows from Laravel controllers directly to Svelte components without designing an API.
To add it to an existing project, install the server-side and client-side pieces 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
Next, 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(), ],})
Start the Inertia app from resources/js/app.ts. The @inertiajs/vite plugin handles page auto-resolution and mounting, so only a minimal entry point is needed.
import { createInertiaApp } from '@inertiajs/svelte'createInertiaApp()
For manual installation details (root template configuration, middleware registration, etc.), see the Inertia official docs.
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 Components (SFCs), but there’s less markup and script to write. Because <style> is scoped by default, you don’t need to worry about class name collisions.
<script lang="ts"> let isLoggedIn = $state(false) let role = $state('editor')</script>{#if isLoggedIn} <p>Welcome!</p>{:else if role === 'admin'} <p>Logged in as admin</p>{:else} <a href="/login">Log in</a>{/if}
Equivalent to Vue’s v-if / v-else or React’s ternary operator.
The <Link> component from @inertiajs/svelte routes page transitions via XHR, avoiding a full browser reload.
<script lang="ts"> import { Link } from '@inertiajs/svelte'</script><!-- Basic link --><Link href="/posts">Posts</Link><!-- Link with POST method (e.g. delete) --><Link href="/posts/1" method="delete" as="button"> Delete</Link><!-- Preload (fetch ahead of time on hover) --><Link href="/posts/1" preload>View post</Link>
You write it like a normal <a> tag, but Inertia swaps only the page component behind the scenes, giving you an SPA-like feel.
The <Form> component from @inertiajs/svelte is the recommended form submission style used in the starter kit auth screens. Specify action and method as props and write the interior with a {#snippet}.
{#snippet children({ errors, processing })} is Svelte’s snippet syntax—a content block passed to a component (equivalent to Vue slots or React render props). errors and processing are computed and provided automatically by the Form component. Form fields use HTML-native name attributes rather than bind:value, and the browser’s standard form data collection does the work.
The starter kit uses Wayfinder to manage routes as objects. store.form() returns an object containing the route object’s 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 contents --> {/snippet}</Form>
Fields listed in resetOnSuccess are automatically reset on successful submission. Use it for fields like password fields that should be cleared after submit.
Without Wayfinder, pass the URL directly like action="/login" and it behaves the same.
Here are the main properties on the object returned by useForm.
Property / method
Description
form.data
The form’s data object
form.errors
Validation errors (access by field name)
form.processing
true while submitting (used to disable the button)
form.isDirty
true if changed from initial values
form.post(url)
Submit via POST
form.put(url)
Submit via PUT (for updates)
form.delete(url)
Submit via DELETE
form.reset()
Reset the form to initial values
When validation errors are returned, useForm displays them while preserving the input values. Combined with bind:value, it delivers a seamless form experience.
Because shared data is included on every request, keep it to the minimum needed. Wrapping in fn() for lazy evaluation ensures it’s only evaluated when actually accessed.
<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 become automatically reactive. When the value changes, the DOM updates automatically. It corresponds to Vue’s ref or React’s useState, but you don’t need .value—just assign to update state.
<script lang="ts"> let query = $state('') // Runs whenever query changes $effect(() => { console.log('Query changed:', query) // You can return a cleanup function return () => { console.log('Cleanup') } })</script><input bind:value={query} placeholder="Search..." />
$effect runs each time a $state dependency changes. It corresponds to React’s useEffect, but you don’t need to specify a dependency array—the $state variables used are tracked automatically.
The starter kit includes shadcn-svelte. shadcn-svelte is a component library with the same design philosophy as shadcn/ui for React—copy the code into your project and freely customize.
The starter kit already includes commonly used components like Button, Input, Card, Dialog, and Dropdown. Add more as needed with npx shadcn-svelte@latest add.
Svelte shines when paired with Laravel, especially in a “modern monolith” configuration through Inertia. The compiler-based design keeps bundle sizes small, and the runes syntax makes reactivity explicit and easy to understand.
Element
Role
Laravel controller
Routing, data retrieval, validation
Inertia::render()
Pass data from controller to Svelte component
Svelte page component
Receive props with $props() and render UI
useForm
Form state management, submission, error display
Link component
Page transitions without a full reload
usePage().props
Access shared data
shadcn-svelte
Standard component library
Inertia × Svelte combines the simplicity of a Laravel backend with Svelte’s compact style, offering a great development experience. Creating a project from the starter kit gets you started immediately, auth screens included.
Inertia.js official docs
See the official documentation for all Inertia v3 features.