laravel/blank-vue-starter-kit
Laravel + Vue starter kit for Inertia apps: classic Laravel routing/controllers with a modern Vue SPA frontend. Includes Vue, TypeScript, Tailwind, and Vite setup, but no auth scaffolding—start building your own UI fast.
Installation
composer create-project laravel/blank-vue-starter-kit my-app
cd my-app
npm install
npm run dev
php, composer, and node are installed.php artisan serve in a separate terminal to start the Laravel backend.First Use Case: Create a Vue Page
resources/js/Pages/ and create a new Vue component (e.g., Welcome.vue).routes/web.php:
Route::get('/', function () {
return Inertia::render('Welcome');
});
http://localhost:8000 to see your new page.Key Directories to Explore
resources/js/Pages/ – Vue components tied to Laravel routes.resources/js/Layouts/ – Shared layouts (e.g., AppLayout.vue).resources/js/Composables/ – Reusable logic (e.g., useForm, usePage).vite.config.js – Vite configuration for asset handling.Server-Side Routing with Vue
routes/web.php) to map to Vue components via Inertia::render().Route::get('/dashboard', [DashboardController::class, 'index'])
->name('dashboard');
<!-- resources/js/Pages/Dashboard.vue -->
<template>
<AppLayout>
<h1>Dashboard</h1>
</AppLayout>
</template>
State Management
usePage composable to access Laravel data:
<script setup>
import { usePage } from '@inertiajs/vue3';
const props = defineProps({
// Access via `usePage().props`
});
</script>
npm install pinia
Configure in resources/js/app.ts:
import { createPinia } from 'pinia';
app.use(createPinia());
API Calls with useForm
<script setup>
import { useForm } from '@inertiajs/vue3';
const form = useForm({
name: '',
});
const submit = () => form.post('/submit');
</script>
Tailwind CSS
tailwind.config.js:
module.exports = {
content: [
'./resources/**/*.blade.php',
'./resources/**/*.vue',
],
};
TypeScript Support
.vue files default to TypeScript. Define props/interfaces:
<script setup lang="ts">
interface Props {
title: string;
}
defineProps<Props>();
</script>
Inertia Page Loading
inertia:before or inertia:progress events in AppLayout.vue:
<template>
<div @inertia:before="startLoading" @inertia:progress="updateProgress">
<!-- Content -->
</div>
</template>
<script setup>
import { ref } from 'vue';
const progress = ref(0);
const startLoading = () => { progress.value = 0; };
const updateProgress = (e: { progress: number }) => { progress.value = e.progress; };
</script>
Vite HMR (Hot Module Replacement)
.vue files may not reflect immediately.npm run dev is running and check browser console for errors. Restart Vite if needed:
npm run dev -- --force
TypeScript Errors
npm install --save-dev @types/inertia @pinia/testing
Shared Layouts
AppLayout.vue wraps only the <slot>:
<template>
<div>
<header>...</header>
<main>
<slot /> <!-- Only child content -->
</main>
</div>
</template>
Inertia Visits
resources/js/app.ts:
import { router } from '@inertiajs/vue3';
router.on('navigate', () => console.log('Page changed'));
Laravel + Vue Dev Tools
Environment-Specific Config
.env variables for API endpoints or feature flags:
VITE_APP_API_URL=https://api.example.com
const apiUrl = import.meta.env.VITE_APP_API_URL;
Custom Inertia Plugins
resources/js/app.ts:
import { plugin } from '@inertiajs/vue3';
app.use(plugin, {
resolve: (name) => require(`./Pages/${name}.vue`),
page: { component: 'AppLayout' },
});
API Middleware
resources/js/app.ts for global API calls:
app.config.globalProperties.$api = {
get: (url: string) => axios.get(url),
post: (url: string, data: any) => axios.post(url, data),
};
Testing
tests/Feature:
use Inertia\Testing\Assert;
$response = $this->get('/dashboard');
$response->assertInertia(fn (Assert $page) =>
$page->component('Dashboard')
);
Multi-Page Apps (MPA) Fallback
Route::get('/legacy', function () {
return view('legacy.page');
})->middleware('disable.inertia');
How can I help you explore Laravel packages today?