> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Introductie Vue.js — de basis voor Inertia × Laravel

> Een introductie van Vue.js, het JS-framework dat Laravel-gebruikers het beste kennen. Van een overzicht van de Options API / Composition API tot paginacomponenten, useForm en gedeelde data met Inertia v3 × Vue 3, praktisch uitgelegd.

## Wat is Vue.js

Vue.js (hierna Vue) is een progressief JavaScript-framework voor het bouwen van gebruikersinterfaces. "Progressief" betekent dat je klein kunt beginnen en naar behoefte functies kunt toevoegen: het leent zich zowel voor gedeeltelijke integratie in bestaande HTML-pagina's als voor het bouwen van grootschalige SPA's.

De kern van Vue is **reactiviteit**. Verandert de data, dan wordt de DOM automatisch bijgewerkt, zodat je als ontwikkelaar niet handmatig hoeft te beheren "wanneer welk element moet worden bijgewerkt".

<Info>
  Deze pagina behandelt de combinatie van Vue 3 en Inertia v3. De starter kits van Laravel 13 gebruiken deze opzet standaard.
</Info>

### Options API en Composition API

Vue 3 biedt twee stijlen om componenten te schrijven: de **Options API** en de **Composition API**.

De **Options API** is de klassieke stijl uit Vue 2. Je definieert componenten met een optiesobject met `data`, `methods`, `computed`, `mounted` en meer.

```vue theme={null}
<!-- Voorbeeld van de Options API -->
<script>
export default {
    data() {
        return { count: 0 }
    },
    methods: {
        increment() {
            this.count++
        }
    }
}
</script>

<template>
    <button @click="increment">{{ count }}</button>
</template>
```

De **Composition API** is de nieuwe stijl die in Vue 3 werd geïntroduceerd. In combinatie met de `<script setup>`-syntaxis schrijf je nog beknopter. De logica is beter herbruikbaar en de samenwerking met TypeScript is uitstekend.

```vue theme={null}
<!-- Voorbeeld van de Composition API (<script setup>) -->
<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
    count.value++
}
</script>

<template>
    <button @click="increment">{{ count }}</button>
</template>
```

<Tip>
  In de Inertia × Laravel starter kits is de Composition API-stijl met `<script setup>` de standaard. Alle voorbeelden op deze pagina zijn daarom met `<script setup>` geschreven.
</Tip>

***

## De positie binnen Laravel

### Geschiedenis

De relatie tussen Vue en Laravel gaat ver terug en begon toen Vue in **Laravel 5.3 (2016)** werd gekozen als standaard frontendframework. De `package.json` van toen bevatte Vue, en er werd zelfs een voorbeeldcomponent `resources/js/components/ExampleComponent.vue` meegeleverd.

```mermaid theme={null}
timeline
    title Laravel en Vue door de jaren heen
    2016 : Laravel 5.3 — Vue als default gekozen
    2019 : Laravel 6 — Vue-scaffold afgesplitst naar de package laravel/ui
    2021 : Laravel 8 — Jetstream + Inertia (Vue) starter kit verschijnt
    2022 : Laravel 9 — Overstap naar Vite
    2025 : Laravel 12 — Vernieuwde starter kits (Vue / React)
    2026 : Laravel 13 — Starter kits met Inertia v3
```

Bij **Laravel 6 (2019)** werd de authenticatiescaffolding afgesplitst naar de package `laravel/ui`, waar ook de Vue-scaffold naartoe verhuisde. Tegenwoordig is het kiezen van de Inertia + Vue-opzet via de starter kits van `laravel new` de gangbare stijl.

Voor Laravel-gebruikers is Vue het meest vertrouwde JS-framework, met een overvloed aan leermateriaal.

### De huidige mainstream: Inertia × Vue

De belangrijkste manier om Vue in Laravel te gebruiken is tegenwoordig **Inertia × Vue**. Inertia realiseert een "moderne monoliet"-architectuur waarbij je zonder een API te ontwerpen data rechtstreeks vanuit Laravel-controllers aan Vue-componenten doorgeeft.

```mermaid theme={null}
graph LR
    Browser["Browser"]
    Inertia["Inertia.js<br>(adapterlaag)"]
    Laravel["Laravel<br>(controller)"]
    Vue["Vue<br>(paginacomponent)"]

    Browser <-->|XHR / volledige paginalading| Inertia
    Inertia <-->|Inertia-response| Laravel
    Inertia -->|props| Vue
    Vue -->|rendering| Browser
```

***

## Setup

### Via een starter kit (aanbevolen)

Voor een nieuw project is een starter kit de makkelijkste route.

```shell theme={null}
laravel new my-app
```

Kies je in de interactieve prompt voor **Vue**, dan wordt dit allemaal automatisch opgezet:

* `inertiajs/inertia-laravel` (server-side adapter)
* `@inertiajs/vue3` (clientadapter)
* `vue` (Vue 3 zelf)
* `@vitejs/plugin-vue` (Vite-plugin)
* De middleware `HandleInertiaRequests`
* Authenticatieschermen zoals inloggen en registreren (al geïmplementeerd met Inertia + Vue)

### Handmatige installatie

Voeg je Inertia toe aan een bestaand project, dan installeer je de server- en clientzijde afzonderlijk.

```shell theme={null}
# Serverzijde (PHP)
composer require inertiajs/inertia-laravel

# Clientzijde (JavaScript)
npm install @inertiajs/vue3 vue
npm install --save-dev @vitejs/plugin-vue
```

Voeg vervolgens de Vue-plugin toe aan `vite.config.js`.

```js theme={null}
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
    ],
})
```

Start de Inertia-app in `resources/js/app.js`.

```js theme={null}
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'

createInertiaApp({
    resolve: (name) =>
        resolvePageComponent(
            `./pages/${name}.vue`,
            import.meta.glob('./pages/**/*.vue'),
        ),
    setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
            .use(plugin)
            .mount(el)
    },
})
```

<Info>
  Zie de [officiële Inertia-documentatie](https://inertiajs.com/installation) voor de details van een handmatige installatie (zoals het instellen van de roottemplate en het registreren van de middleware).
</Info>

***

## Directorystructuur

In de starter kits plaats je Vue-paginacomponenten in de directory `resources/js/pages/`.

```
resources/js/
├── app.js             # Startpunt van de Inertia-app
├── bootstrap.js
├── components/        # Herbruikbare UI-componenten
│   ├── NavBar.vue
│   └── ...
├── layouts/           # Layoutcomponenten
│   ├── AppLayout.vue
│   └── AuthLayout.vue
└── pages/             # Inertia-paginacomponenten (corresponderen met controllernamen)
    ├── Auth/
    │   ├── Login.vue
    │   └── Register.vue
    ├── Dashboard.vue
    └── Posts/
        ├── Index.vue
        ├── Create.vue
        └── Show.vue
```

Schrijf je `Inertia::render('Posts/Index', [...])`, dan is `resources/js/pages/Posts/Index.vue` de bijbehorende component.

***

## De templatesyntaxis van Vue

Hier zijn de basisdirectives die je nodig hebt om de code van de starter kits te kunnen lezen en schrijven.

#### `{{ }}` — variabelen invoegen

Met dubbele accolades voeg je JavaScript-waarden of -expressies in de template in.

```vue theme={null}
<script setup>
const name = 'wereld'
const count = 3
</script>

<template>
    <p>Hallo, {{ name }}!</p>
    <p>Het dubbele is {{ count * 2 }}</p>
</template>
```

#### `v-if` — voorwaarden

```vue theme={null}
<template>
    <p v-if="isLoggedIn">Welkom!</p>
    <p v-else-if="role === 'admin'">Ingelogd als beheerder</p>
    <a v-else href="/login">Inloggen</a>
</template>
```

Dit komt overeen met `{#if}` in Svelte of de ternaire operator in React.

#### `v-for` — lijsten renderen

```vue theme={null}
<template>
    <ul>
        <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
    </ul>
</template>
```

Geef altijd `:key` op voor efficiënte diff-updates. Dit komt overeen met `Array.map()` in React.

#### `v-model` — two-way binding

Met `v-model` synchroniseer je de waarde van een formulierelement in twee richtingen met een reactieve variabele.

```vue theme={null}
<script setup>
import { ref } from 'vue'

const title = ref('')
const agreed = ref(false)
const role = ref('viewer')
</script>

<template>
    <!-- Tekstinvoer -->
    <input v-model="title" type="text" />
    <p>Invoer: {{ title }}</p>

    <!-- Checkbox -->
    <input v-model="agreed" type="checkbox" />
    <p>Akkoord: {{ agreed }}</p>

    <!-- Selectbox -->
    <select v-model="role">
        <option value="viewer">Lezer</option>
        <option value="editor">Redacteur</option>
        <option value="admin">Beheerder</option>
    </select>
</template>
```

#### `:` (v-bind) en `@` (v-on)

* `:attr="value"` — bindt een dynamische waarde aan een HTML-attribuut (verkorte vorm van `v-bind:attr`)
* `@event="handler"` — registreert een eventlistener (verkorte vorm van `v-on:event`)

```vue theme={null}
<template>
    <!-- Dynamische attribuutbinding -->
    <img :src="imageUrl" :alt="imageAlt" />

    <!-- Eventhandlers -->
    <button @click="handleClick">Klik</button>
    <form @submit.prevent="handleSubmit">...</form>
</template>
```

***

## De basis van paginacomponenten

Een Inertia-paginacomponent is een gewone Vue-component. De data die je vanuit de Laravel-controller doorgeeft, ontvang je als props.

### Controller

```php theme={null}
// app/Http/Controllers/PostController.php
use Inertia\Inertia;
use App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        return Inertia::render('Posts/Index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }
}
```

### Vue-paginacomponent

```vue theme={null}
<!-- resources/js/pages/Posts/Index.vue -->
<script setup>
import { Link } from '@inertiajs/vue3'

defineProps({
    posts: Object,
})
</script>

<template>
    <div>
        <h1>Postoverzicht</h1>
        <article v-for="post in posts.data" :key="post.id">
            <h2>
                <Link :href="`/posts/${post.id}`">{{ post.title }}</Link>
            </h2>
            <p>{{ post.created_at }}</p>
        </article>
    </div>
</template>
```

Je hoeft de props alleen te declareren met `defineProps()` en kunt de data uit de controller direct in de template gebruiken. Een REST API definiëren is niet nodig.

***

## De `Link`-component

Met de `<Link>`-component van `@inertiajs/vue3` verlopen paginanavigaties via XHR, zodat een volledige browserreload wordt vermeden.

```vue theme={null}
<script setup>
import { Link } from '@inertiajs/vue3'
</script>

<template>
    <!-- Basislink -->
    <Link href="/posts">Postoverzicht</Link>

    <!-- Link met de POST-methode (bijv. verwijderen) -->
    <Link href="/posts/1" method="delete" as="button" type="button">
        Verwijderen
    </Link>

    <!-- Preload (vooraf ophalen bij hover) -->
    <Link href="/posts/1" preload>Post bekijken</Link>
</template>
```

Je schrijft hem net als een gewone `<a>`-tag, maar op de achtergrond vervangt Inertia alleen de paginacomponent, waardoor het aanvoelt als een SPA.

***

## De `Form`-component

De `<Form>`-component van `@inertiajs/vue3` is de aanbevolen stijl voor formulierverzending die in de authenticatieschermen van de starter kits wordt gebruikt. Je geeft `action` en `method` op als props en krijgt via `v-slot` toegang tot `errors` en `processing`.

### Basisgebruik

```vue theme={null}
<script setup>
import { Form } from '@inertiajs/vue3'
</script>

<template>
    <Form action="/posts" method="post" class="flex flex-col gap-4" v-slot="{ errors, processing }">
        <div>
            <label for="title">Titel</label>
            <input id="title" name="title" type="text" required />
            <p v-if="errors.title" class="error">{{ errors.title }}</p>
        </div>

        <div>
            <label for="content">Inhoud</label>
            <textarea id="content" name="content"></textarea>
            <p v-if="errors.content" class="error">{{ errors.content }}</p>
        </div>

        <button type="submit" :disabled="processing">
            {{ processing ? 'Versturen...' : 'Posten' }}
        </button>
    </Form>
</template>
```

`v-slot="{ errors, processing }"` is de scoped-slot-syntaxis van Vue: de `Form`-component berekent deze waarden automatisch en geeft ze door. Voor de formuliervelden gebruik je geen `v-model` maar het native HTML-attribuut `name`, zodat de standaard formulierdataverzameling van de browser werkt.

### Het patroon van de starter kits

De starter kits gebruiken [Wayfinder](/nl/blog/wayfinder-introduction) om routes als objecten te beheren. `store.form()` geeft een object terug met de `action` en `method` van het routeobject, dat je via `v-bind` in `<Form>` spreadt.

```vue theme={null}
<script setup lang="ts">
import { Form } from '@inertiajs/vue3'
import { store } from '@/routes/login'
</script>

<template>
    <Form
        v-bind="store.form()"
        :reset-on-success="['password']"
        v-slot="{ errors, processing }"
        class="flex flex-col gap-6"
    >
        <!-- Inhoud van het formulier -->
    </Form>
</template>
```

Velden die je opgeeft in `reset-on-success` worden bij een geslaagde verzending automatisch gereset. Gebruik dit voor velden die je na het verzenden leeg wilt maken, zoals wachtwoordvelden.

<Info>
  Gebruik je Wayfinder niet, dan werkt het net zo goed als je direct een URL doorgeeft, zoals `action="/login"`.
</Info>

***

## De `useForm`-helper

Voor formulierverwerking gebruik je de `useForm`-helper van `@inertiajs/vue3`. Statebeheer van het formulier, verzenden en het tonen van validatiefouten implementeer je hiermee eenvoudig.

### Aan de controllerkant

```php theme={null}
// app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
        ]);

        Post::create($validated + ['user_id' => auth()->id()]);

        return redirect()->route('posts.index')
            ->with('success', 'Post aangemaakt.');
    }
}
```

### Vue-formuliercomponent

```vue theme={null}
<!-- resources/js/pages/Posts/Create.vue -->
<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
    title: '',
    content: '',
})

function submit() {
    form.post('/posts')
}
</script>

<template>
    <form @submit.prevent="submit">
        <div>
            <label>Titel</label>
            <input v-model="form.title" type="text" />
            <p v-if="form.errors.title" class="error">{{ form.errors.title }}</p>
        </div>

        <div>
            <label>Inhoud</label>
            <textarea v-model="form.content"></textarea>
            <p v-if="form.errors.content" class="error">{{ form.errors.content }}</p>
        </div>

        <button type="submit" :disabled="form.processing">
            {{ form.processing ? 'Versturen...' : 'Posten' }}
        </button>
    </form>
</template>
```

De belangrijkste properties van het object dat `useForm` teruggeeft:

| Property / methode | Beschrijving                                                           |
| ------------------ | ---------------------------------------------------------------------- |
| `form.data`        | Het dataobject van het formulier                                       |
| `form.errors`      | Validatiefouten (toegankelijk per veldnaam)                            |
| `form.processing`  | `true` tijdens het verzenden (gebruik dit om de knop uit te schakelen) |
| `form.isDirty`     | `true` als er iets is gewijzigd ten opzichte van de beginwaarden       |
| `form.post(url)`   | Verzenden met een POST-request                                         |
| `form.put(url)`    | Verzenden met een PUT-request (bijwerken)                              |
| `form.delete(url)` | Verzenden met een DELETE-request                                       |
| `form.reset()`     | Zet het formulier terug naar de beginwaarden                           |

Komt er een validatiefout terug, dan toont `useForm` de fouten terwijl de ingevoerde inhoud behouden blijft. In combinatie met `v-model` krijg je een naadloze formulierervaring.

***

## Gedeelde data (shared data)

Data die je op elke pagina nodig hebt (zoals de ingelogde gebruiker en flashberichten) definieer je in de methode `share()` van de middleware `HandleInertiaRequests`.

```php theme={null}
// app/Http/Middleware/HandleInertiaRequests.php
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'auth' => [
                'user' => $request->user()
                    ? $request->user()->only('id', 'name', 'email')
                    : null,
            ],
            'flash' => [
                'success' => $request->session()->get('success'),
                'error'   => $request->session()->get('error'),
            ],
        ]);
    }
}
```

Om vanuit een Vue-component bij de gedeelde data te komen, gebruik je `usePage()`.

```vue theme={null}
<script setup>
import { computed } from 'vue'
import { usePage } from '@inertiajs/vue3'

const page = usePage()

// Toegang tot gedeelde data
const user = computed(() => page.props.auth.user)
const flash = computed(() => page.props.flash)
</script>

<template>
    <header>
        <span v-if="user">{{ user.name }}</span>
        <span v-else>Gast</span>
    </header>

    <div v-if="flash.success" class="alert-success">
        {{ flash.success }}
    </div>
</template>
```

<Info>
  Gedeelde data wordt bij elke request meegestuurd; beperk deze daarom tot het strikt noodzakelijke. Gebruik je lazy evaluation met `fn()`, dan wordt de data alleen geëvalueerd wanneer er daadwerkelijk toegang toe is.
</Info>

***

## De basis van de reactiviteit van Vue 3

Hier zijn de reactiviteits-API's van Vue 3 die je moet kennen om met Inertia × Vue te ontwikkelen.

### `ref` — primitieve reactieve waarden

```vue theme={null}
<script setup>
import { ref } from 'vue'

const count = ref(0)
const isOpen = ref(false)

// Toegang via .value (in de template niet nodig)
count.value++
</script>

<template>
    <p>{{ count }}</p>
    <button @click="isOpen = !isOpen">Toggle</button>
</template>
```

### `computed` — berekende properties

```vue theme={null}
<script setup>
import { ref, computed } from 'vue'

const posts = ref([])

const publishedPosts = computed(() =>
    posts.value.filter(post => post.published)
)
</script>
```

### `onMounted` — verwerking na het mounten

```vue theme={null}
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
    console.log('De component is gemount')
})
</script>
```

***

## Samenvatting

Vue.js sluit uitstekend aan bij Laravel en komt vooral tot zijn recht in de "moderne monoliet"-opzet via Inertia.

| Element             | Rol                                                    |
| ------------------- | ------------------------------------------------------ |
| Laravel-controller  | Routing, data ophalen, validatie                       |
| `Inertia::render()` | Geeft data door van de controller aan de Vue-component |
| Vue-paginacomponent | Ontvangt props en rendert de UI                        |
| `useForm`           | Statebeheer, verzenden en fouten tonen bij formulieren |
| `Link`-component    | Paginanavigatie zonder volledige reload                |
| `usePage().props`   | Toegang tot gedeelde data                              |

Met Inertia × Vue profiteer je zowel van de eenvoud van een Laravel-backend als van de reactieve UI van Vue. Maak je een project aan met de starter kit, dan kun je direct beginnen met ontwikkelen, inclusief authenticatieschermen.

<Card title="Officiële Inertia.js-documentatie" icon="book-open" href="https://inertiajs.com">
  Raadpleeg de officiële documentatie voor alle functies van Inertia v3.
</Card>


## Related topics

- [Introductie React — de basis voor Inertia × Laravel](/nl/blog/react-introduction.md)
- [Introductie Svelte — de basis voor Inertia × Laravel](/nl/blog/svelte-introduction.md)
- [Frontend](/nl/frontend.md)
- [Een SPA bouwen met Inertia.js](/nl/blog/inertia-introduction.md)
- [Laravel Fortify en de starter kits](/nl/advanced/fortify.md)
