Home About Skills Products Work
Projects Services Experience
Learn
Tutorials Courses Blogs Resources
Contact
Blog · Vue.js

Building a Full-Stack App with Vue.js and Laravel (Inertia.js)

Building a Full-Stack App with Vue.js and Laravel (Inertia.js)
Advertisement

Inertia.js lets you build a Vue.js frontend backed by a Laravel application without building a separate JSON API — no REST endpoints, no client-side routing library, just Laravel controllers returning Vue pages directly. It's the fastest path to a modern SPA feel from an existing Laravel codebase.

The Core Idea

A normal SPA needs a JSON API and a client-side router to reconstruct pages. Inertia skips both: your Laravel controller returns an Inertia response that names a Vue component and passes it props, exactly like returning a Blade view — except the "view" is a full Vue single-file component, and navigation between pages happens via XHR without a full page reload.

Installing Inertia

composer require inertiajs/inertia-laravel
npm install @inertiajs/vue3
// resources/js/app.js
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';

createInertiaApp({
  resolve: (name) => import(`./Pages/${name}.vue`),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el);
  },
});

Returning a Page from a Controller

class PostController extends Controller
{
    public function index()
    {
        return inertia('Posts/Index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }
}
<!-- resources/js/Pages/Posts/Index.vue -->
<script setup>
defineProps({ posts: Object });
</script>

<template>
  <div v-for="post in posts.data" :key="post.id">{{ post.title }}</div>
</template>

Notice there's no fetch() anywhere — the props just show up, exactly like a Blade view's variables would.

Forms Without Writing Fetch Logic

<script setup>
import { useForm } from '@inertiajs/vue3';
const form = useForm({ title: '', body: '' });

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

<template>
  <form @submit.prevent="submit">
    <input v-model="form.title" />
    <span v-if="form.errors.title">{{ form.errors.title }}</span>
    <button :disabled="form.processing">Save</button>
  </form>
</template>

Laravel's normal validation errors flow straight into form.errors — the same $request->validate() you'd write for a Blade form works completely unchanged.

When Inertia Is the Right Choice

Inertia is an excellent fit when Laravel and the frontend are the same team, same deploy, same codebase — you get SPA navigation and a modern component model without maintaining a separate API. If you need a truly independent frontend (a separate mobile app, a third-party integration), a real JSON API plus Sanctum is still the better architecture.

Vue 3 Composition API: A Practical Introduction

Vue 3 Composition API: A Practical Introduction

setup(), ref vs reactive, and reusable composables — a practical introduction to Vue 3's Composition API for developers coming from Options API or another framework.

State Management in Vue with Pinia

State Management in Vue with Pinia

Why Pinia replaced Vuex as Vue's recommended state management library, and how to set up stores in both Options and Composition API styles.

Esc