Creating Laravel + Vue + Inertia project

A little step-by-step guide

Update. Code snippets updated to use Inertia.js 1.0.

Recently, I've been learning what's new in the Laravel world, and I've got pretty excited about Laravel, Vue.js and Inertia.js combo.

Really, it combines the smoothness of single-page applications (SPA) and SEO friendliness of traditional server-rendered websites.

However, it's not trivial to set it up from scratch - you have to check the docs of all three technologies. So, I've made a little step-by-step guide for myself and made sure that it works.

Hopefully, it'll be useful for you, too.

And if you are impatient, like me, you can grab the working project template from GitHub.

OK, let's get started!

Create new Laravel project

# Create a project
cd ~/projects
laravel new ssr
cd ssr

# Serve the application on the PHP development server 
# Keep it running in a separate terminal session
php artisan serve

# Test it by opening in the browser

Install and configure the server-side Inertia

  1. Install the Inertia server-side package:

     composer require inertiajs/inertia-laravel
  2. Setup the root template, resources/views/app.blade.php that will be loaded on the first page visit:

     <!DOCTYPE html>
     <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
             <meta charset="utf-8">
             <meta name="viewport" 
                             content="width=device-width, initial-scale=1">
  3. Create the Inertia middleware that will process AJAX loading of the pages:

     php artisan inertia:middleware
  4. Register the HandleInertiaRequests middleware in your App\Http\Kernel, as the last item in your web middleware group:

     'web' => [
         // ...
  5. Replace the home page route in the routes/web.php with the code that renders a Vue template (we’ll implement the Home component in a minute):

     Route::get('/', function () {
         return inertia('Home');
  6. Remove the resources/views/welcome.blade.php.

Install and configure the client-side Inertia, Vue3 and Tailwind

  1. Install the required Node packages:

     npm install vue @inertiajs/vue3
     npm install --dev @vitejs/plugin-vue tailwindcss autoprefixer
  2. Update your main JavaScript file, resources/js/app.js, to boot your Inertia app:

     import './bootstrap';
     import '../css/app.css';
     import { createApp, h } from 'vue';
     import { createInertiaApp } from '@inertiajs/vue3';
         resolve: name => {
             const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
             return pages[`./Pages/${name}.vue`]
         setup({ el, App, props, plugin }) {
             createApp({ render: () => h(App, props) })
  3. In vite.config.js, configure Vite to handle Vue templates:

     import { defineConfig } from 'vite';
     import laravel from 'laravel-vite-plugin';
     import vue from '@vitejs/plugin-vue';
     export default defineConfig({
         plugins: [
                 input: 'resources/js/app.js',
                 refresh: true,
                 template: {
                     transformAssetUrls: {
                         base: null,
                         includeAbsolute: false,
  4. Add postcss.config.cjs to enable Tailwind CSS post processing:

     module.exports = {
       plugins: {
         tailwindcss: {},
         autoprefixer: {},
  5. Create tailwind.config.js to specify directories that should be handled by Tailwind CSS:

     module.exports = {
         content: [
  6. Add Tailwind CSS styles to your main CSS file, resources/css/app.css:

     @tailwind base;
     @tailwind components;
     @tailwind utilities;
  7. Create the resources/js/Pages/Home.vue component:

     <script setup>
     import { Head } from '@inertiajs/vue3';
             <title>Hello, world!</title>
         <main class="fixed inset-0 grid place-items-center">
             <h1 class="text-2xl">Hello, world!</h1>
  8. Build the JavaScript and CSS assets:

     # Keep it running in a separate terminal session
     npm run dev
  9. If you use PHPStorm, add jsconfig.json to help resolving import ... '@...' statements:

         "compilerOptions": {
             "baseUrl": ".",
             "paths": {
                 "@/*": ["resources/js/*"]
         "exclude": ["node_modules", "public"]