The Laravel Svelte Starter Kit Is Official. Here’s Everything That Ships With It — and Why Svelte 5 Makes It Worth Choosing.

Inertia 2 · Svelte 5 · TypeScript · Tailwind · shadcn-svelte · bits-ui. First-party, February 2026. Here’s the full picture: what’s in the kit, how Svelte 5 runes change the Inertia pattern, and how to wire streaming AI responses from the Laravel AI SDK.

For years, the question “can I use Svelte with Laravel?” had one answer: technically yes, but you’re on your own for the boilerplate. The React and Vue starter kits had full auth scaffolding, component libraries, TypeScript setup, and a tested path from laravel new to running application. Svelte had community packages.

That changed in February 2026. The official Laravel Svelte + Inertia starter kit shipped, sitting right next to the React, Vue, and Livewire starter kits in the docs. First-party, maintained by the Laravel team, patterned exactly after the React and Vue kits.

This is the complete guide: what ships in the kit, how Svelte 5 runes change the Inertia development pattern, streaming AI responses from the Laravel AI SDK, and when to choose Svelte over React or Vue.


What Ships in the Kit

The Svelte starter kit is built with Inertia 2, Svelte 5, Tailwind, and shadcn-svelte. It also includes the bits-ui component library.

# Full kit — authentication scaffolding included
laravel new my-app --using=svelte

# Blank kit — no auth, just the stack wired up
laravel new my-app --using=svelte-blank

Out of the box you get:

  • Full authentication — register, login, password reset, email verification, profile management
  • Two layouts — sidebar (default) and header, toggle at install time
  • Three auth layouts — simple, card, or split — configurable
  • Dark/light/system mode — built in, no configuration
  • TypeScript — full support by default
  • GitHub Actions workflows — automated testing and code linting preconfigured
  • shadcn-svelte + bits-ui — component library ready to use

The majority of the frontend code lives in resources/js/:

resources/js/
├── components/     # Reusable Svelte components
├── layouts/        # Application layouts
├── lib/            # Utility functions, config, Svelte rune modules
├── pages/          # Page components (one per route)
└── types/          # TypeScript definitions

All of the code lives in your application — you own it entirely. Modify anything, from logos to auth logic.

Adding shadcn-svelte components

# Publish a component into your project
npx shadcn-svelte@latest add switch
# → resources/js/components/ui/switch/switch.svelte

Svelte 5 Runes: The Pattern That Changes Everything

If you haven’t used Svelte 5 yet, the runes system is the biggest shift since Svelte 3. It replaces the implicit reactive declarations (let count = 0 was reactive) with explicit rune functions. The result: reactivity is predictable, composable across files, and works correctly inside classes and modules — not just components.

Here’s how the key runes work in the Inertia + Laravel context:

$state — reactive state

<script lang="ts">
    // Before Svelte 5 — implicit reactivity
    let count = 0;
    let user = { name: '' };  // mutations weren't reactive

    // Svelte 5 — explicit, works anywhere
    let count = $state(0);
    let form = $state({ name: '', email: '', message: '' });
</script>

<input bind:value={form.name} />
<p>Hello {form.name}</p>

$state is deeply reactive by default — mutations to nested objects and arrays are tracked automatically.

$props — typed component props

<!-- InvoiceCard.svelte -->
<script lang="ts">
    interface Props {
        invoice: App.Invoice;
        onPaid?: (id: number) => void;
    }

    let { invoice, onPaid }: Props = $props();
</script>

<div>
    <h3>{invoice.reference}</h3>
    <p>{invoice.formatted_amount}</p>
    {#if invoice.status !== 'paid'}
        <button onclick={() => onPaid?.(invoice.id)}>Mark paid</button>
    {/if}
</div>

$props replaces export let — props are typed, destructured, and optional values are handled cleanly.

$derived — computed values

<script lang="ts">
    let { invoices }: { invoices: App.Invoice[] } = $props();

    // Reactive derived values — recomputes when invoices changes
    let total = $derived(
        invoices.reduce((sum, inv) => sum + inv.amount_pence, 0)
    );

    let overdueCount = $derived(
        invoices.filter(inv => inv.is_overdue).length
    );

    let formattedTotal = $derived(
        '$' + (total / 100).toFixed(2)
    );
</script>

<div>
    <p>{invoices.length} invoices · {formattedTotal} total · {overdueCount} overdue</p>
</div>

$derived replaces $: reactive statements. The key improvement: it’s a value, not a side effect — use it in templates directly, pass it to child components, compose it.

$effect — side effects with cleanup

<script lang="ts">
    let { isOpen } = $props();

    // Runs when isOpen changes, cleanup on re-run or destroy
    $effect(() => {
        if (isOpen) {
            document.body.style.overflow = 'hidden';
            return () => {
                document.body.style.overflow = '';
            };
        }
    });
</script>

$effect is explicit about side effects. The return value is a cleanup function — no more onDestroy scattered across components for effect teardown.

Rune stores — composable, file-portable reactivity

The biggest architectural win in Svelte 5: runes work in .svelte.ts files, not just components. You can extract shared state into modules:

// lib/useInvoiceFilter.svelte.ts
export function useInvoiceFilter(initialInvoices: App.Invoice[]) {
    let invoices = $state(initialInvoices);
    let statusFilter = $state<string>('all');

    let filtered = $derived(
        statusFilter === 'all'
            ? invoices
            : invoices.filter(inv => inv.status === statusFilter)
    );

    function setFilter(status: string) {
        statusFilter = status;
    }

    return { filtered, statusFilter, setFilter };
}
<!-- InvoiceIndex.svelte -->
<script lang="ts">
    let { invoices }: { invoices: App.Invoice[] } = $props();

    const { filtered, statusFilter, setFilter } = useInvoiceFilter(invoices);
</script>

This is the pattern the lib/ directory in the starter kit is designed for — composable rune modules that work like React hooks but without the Rules of Hooks.


Inertia Pages With Svelte 5

A complete Inertia page component in Svelte 5 looks clean and typed:

<!-- pages/Invoices/Index.svelte -->
<script lang="ts">
    import { router } from '@inertiajs/svelte';
    import AppLayout from '@/layouts/app-layout.svelte';
    import InvoiceCard from '@/components/invoice-card.svelte';

    interface Props {
        invoices: {
            data: App.Invoice[];
            links: App.PaginationLinks;
            meta: App.PaginationMeta;
        };
        filters: { status: string };
    }

    let { invoices, filters }: Props = $props();

    let statusFilter = $state(filters.status ?? 'all');

    function applyFilter() {
        router.get(route('invoices.index'), {
            status: statusFilter === 'all' ? undefined : statusFilter
        }, {
            preserveState: true,
            replace: true,
        });
    }

    // Deferred props from Inertia v2
    // <Deferred data="recentActivity"> handled in the component
</script>

<AppLayout title="Invoices">
    <div class="space-y-4">
        <select bind:value={statusFilter} onchange={applyFilter}>
            <option value="all">All</option>
            <option value="paid">Paid</option>
            <option value="overdue">Overdue</option>
        </select>

        {#each invoices.data as invoice (invoice.id)}
            <InvoiceCard {invoice} />
        {/each}
    </div>
</AppLayout>

Streaming from the Laravel AI SDK to Svelte

The Laravel AI SDK’s .stream() method returns a server-sent event stream. Consuming it in Svelte is straightforward with the EventSource API or fetch + ReadableStream:

The Laravel controller

// InvoiceAnalysisController.php
public function analyse(Invoice $invoice): StreamedResponse
{
    return (new InvoiceAnalyst)->stream(
        "Analyse this invoice and summarise the payment risk: {$invoice->toJson()}"
    );
}

The Svelte component consuming the stream

<!-- components/InvoiceAnalysis.svelte -->
<script lang="ts">
    let { invoiceId }: { invoiceId: number } = $props();

    let analysis = $state('');
    let streaming = $state(false);
    let done = $state(false);

    async function startAnalysis() {
        analysis = '';
        streaming = true;
        done = false;

        const response = await fetch(`/invoices/${invoiceId}/analyse`, {
            method: 'POST',
            headers: {
                'X-CSRF-TOKEN': document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')!.content,
                'Accept': 'text/event-stream',
            },
        });

        if (!response.body) return;

        const reader = response.body.getReader();
        const decoder = new TextDecoder();

        while (true) {
            const { done: streamDone, value } = await reader.read();
            if (streamDone) break;

            const chunk = decoder.decode(value, { stream: true });

            // Parse SSE format: "data: token\n\n"
            const lines = chunk.split('\n');
            for (const line of lines) {
                if (line.startsWith('data: ')) {
                    const token = line.slice(6);
                    if (token !== '[DONE]') {
                        analysis += token;
                    }
                }
            }
        }

        streaming = false;
        done = true;
    }
</script>

<div class="space-y-4">
    <button
        onclick={startAnalysis}
        disabled={streaming}
        class="btn-primary"
    >
        {streaming ? 'Analysing…' : 'Analyse Invoice'}
    </button>

    {#if analysis}
        <div class="rounded-lg border p-4">
            <p class="whitespace-pre-wrap">{analysis}</p>
            {#if streaming}
                <span class="animate-pulse">▋</span>
            {/if}
        </div>
    {/if}
</div>

The $state reactivity means every token append to analysis triggers a DOM update automatically — no manual reactivity wiring needed.


Why Choose Svelte Over React or Vue?

This is a genuine question worth answering honestly, since the React and Vue kits are both excellent.

Svelte wins on:

  • Bundle size — Svelte compiles away the runtime. A Svelte app ships less JavaScript than an equivalent React app. For apps where initial load performance matters, this is real.
  • Runes over hooks$state, $derived, $effect are more intuitive than useState, useMemo, useEffect for developers coming from non-React backgrounds. The Rules of Hooks don’t exist in Svelte.
  • Compiled animations — Svelte’s transition and animation system is built in. transition:fly, transition:fade, animate:flip work out of the box with no library.
  • Less boilerplate — a Svelte page component is typically shorter than the equivalent React component for the same feature.

React wins on:

  • Ecosystem — more third-party component libraries, more Stack Overflow answers, more team members who already know it.
  • TypeScript tooling — React + TypeScript has the most mature IDE support of the three options.
  • Hiring — more developers know React than Svelte.

Vue wins on:

  • Familiarity to Laravel devs — Vue has been the Laravel-adjacent frontend for a decade. Lots of existing resources.
  • Options API migration path — for teams with Vue 2 codebases.

The honest recommendation: if your team knows React, use React. If your team knows Vue, use Vue. If you’re starting fresh with no strong preference, Svelte is worth serious consideration — the bundle size advantage and the runes system are both genuinely better than what came before.


The Two Starter Kit Variants

The official kit comes in two flavours:

laravel new my-app --using=svelte — the full kit with authentication, profile management, email verification, password reset, and the complete component library. This is the right choice for apps that need auth from day one.

laravel new my-app --using=svelte-blank — just the stack wired together: Inertia 2, Svelte 5, TypeScript, Tailwind. No auth scaffolding, no component library opinionation. Right for teams that want to bring their own auth or use a package like Fortify separately.


The Bigger Picture

The Laravel Svelte starter kit is the missing piece for a non-trivial portion of the developer community. Svelte 5’s runes make it genuinely more ergonomic than it was in Svelte 4 — the composable .svelte.ts modules in particular bring the “extract to a hook” pattern that React developers rely on into the Svelte world, without the restrictions.

With Inertia 2’s deferred props, async requests, and prefetching (covered in Day 24), the performance story for a Laravel + Svelte + Inertia app is genuinely strong.

The stack is official, maintained, and production-ready. If you’ve been curious about Svelte, this is the moment.


Follow for weekly deep-dives on Laravel, PHP, Vue.js, and the agentic stack.

Leave a Reply

Your email address will not be published. Required fields are marked *