@danielkelly_io

@danielkellyio

Alabama, USA

Daniel Kelly

Lead Instructor @ Vue School

Full Stack developer (10 years)

Husband and Father

Vue,

Inertia,

Laravel

Our story begins with two strangers

Our story begins with two strangers

Vs.

Our story begins with two strangers

Vs.

  • Uses JavaScript
  • Client-Side Rendering
  • SPA Magic
  • SFC-Powered Templating
  • Uses PHP
  • Server-Side Rendering
  • Classic MPA
  • Blade-Powered Templating

It all started when Laravel wanted to build modern, dynamic,

Single-Page Applications

This is when their paths crossed

Laravel sought a partner to collaborate with in achieving this goal.

Vue sparked Laravel's curiosity

In perfect sync, they weave magic

To work harmoniously together, they tried various techniques

Laravel Blade hosting the whole Vue instance

  • Vue serves as the core application.
  • Vue consumes Laravel APIs.
  • Vue Router manages client-side navigation

Option 1

Decoupled Vue.js and Laravel Integration

  • Vue serves as the core application.
  • Vue consumes Laravel APIs.
  • Vue Router manages client-side navigation

Option 2

Decoupled Nuxt.js and Laravel Integration

Option 3

  • Nuxt serves as the core application.
  • Nuxt consumes Laravel APIs.
  • Nuxt handles client and server routing.

The Laravel Backends for Vue.js 3 Course

Laravel Blade hosting Vue SFCs

  • Laravel serves as the core application.
  • Data passed to Vue components via Laravel from the server.
  • Laravel manages server-side navigation

Option 4

Each method, while effective, presented distinct challenges

Each method, while effective, presented distinct challenges

  • Vue navigation overhead
  • Giving up Laravel routing
  • Laravel APIs for Vue
  • SSR Complexity
  • Global components bloat
  • Shipping the component renderer to the browser

So what's a girl to do?

Is it time for Laravel and Vue to break up?

Inertia steps in

And changes everything!

Inertia who?

Inertia is

Not a Framework

Inertia is

The Modern Monolith

Inertia is

The Modern Monolith

  • It serves as a smart approach to SPA development
  • Enables developers to build modern single-page Vue, React, and Svelte apps using classic server-side routing and controllers.
  • Designed for Laravel, Ruby on Rails, and Django developers.

Inertia is

The Modern Monolith

Build single-page apps,

without

building an API.

class UsersController
{
	public function index()
    {
        $users = User::active()->orderByName()->get(['id', 'name', 'email']);

        return Inertia::render('Users', [ 'users' => $users ]);
    }
}
<!-- Pages/Users.vue -->
<script setup>
defineProps({ users: Array })
</script>

<template>
    <div v-for="user in users" :key="user.id">
      <p> {{ user.name }} </p>
    </div>
</template>

But how is Inertia doing it?

https://vueschoo.io

Initial Visit

https://vueschoo.io

🌐

Server

Request

Initial Visit

https://vueschoo.io

🌐

Server

Request

Initial Visit

HTML

Response

https://vueschoo.io

🌐

Server

Request

Initial Visit

<html>
  <head>
      <title>VueSchool</title>
      <link href="/css/app.css" rel="stylesheet">
      <script src="/js/app.js" defer></script>
  </head>
  <body>

  <div 
      id="app" 
      data-page='{"component":"Home","props":{"greet": "true"}'
  ></div>

  </body>
</html>

🔎

The returned HTML contains a root div with data-page attribute

HTML

Response

https://vueschoo.io

🌐

Server

Request

Initial Visit

Response

<div 
	id="app" 
	data-page='{"component":"Home","props":{"greet": "true"}}'
></div>

HTML

Client

// Home.vue
<template>
	<h1 v-if="greet"> Welcome to VueSchool </h1>
</template>

<script setup>
defineProps(['greet'])
</script>

Welcome To VueSchool

Inertia

https://vueschoo.io

Courses

👆

Client-Side Navigation

https://vueschoo.io

Courses

👆

Client-Side Navigation

🌐

Server

XHR Request

X-Inertia: true

Headers

https://vueschoo.io

Courses

👆

Client-Side Navigation

🌐

Server

XHR Request

X-Inertia: true

Headers

Inertia

https://vueschoo.io

Courses

👆

Client-Side Navigation

🌐

Server

XHR Request

X-Inertia: true

Headers

JSON Response

JSON

https://vueschoo.io

Courses

👆

Client-Side Navigation

🌐

Server

XHR Request

X-Inertia: true

Headers

JSON Response

JSON

RESPONSE
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept
X-Inertia: true
{
  "component": "Courses",
  "props": {
    "coursesList": [{}]
  },
  "url": "/courses",
  "version": "version_number"
}

🔎

The returned JSON contains an encoded page object.

https://vueschoo.io

Client-Side Navigation

🌐

Server

XHR Request

X-Inertia: true

Headers

JSON Response

JSON

{
  "component": "Courses",
  "props": {
    "coursesList": [{}]
  },
  "url": "/courses",
  "version": "version_number"
}
// Courses.vue
<template>
  <h1> Courses </h1>
  <ul> <li v-for="course in courses"> course.name </li> </ul>
</template>

<script setup>
defineProps(['courses'])
</script>

Inertia

Courses

<a href="/courses">courses</a>

Courses

👆

https://vueschoo.io

<router-link to="/courses">courses</router-link>
import { Link } from '@inertiajs/vue3'

<Link href="/courses">Courses</Link>

Courses

👆

https://vueschoo.io

  • Lightweight wrapper around a standard anchor tag.
  • It intercepts the click event and prevents full page reloads.

Connects the Vue front-end with the Laravel backend.

You get to use Vue components with Laravel server-side routing

Smooth sailing for Laravel developers who uses Vue

No need for Laravel APIs

SPA Haven

Routing

No need for Vue Router to handle client-side routing

Route::get('/courses', function () {
    return Inertia::render('Courses', [
        'canView' => Route::has('subscribed'),
        'coursesData' => data
    ]);
})->name('courses');

You can create Laravel web routes and use Inertia::render to render the corresponding page

Routing

Route::get('/courses', function () {
    return Inertia::render('Courses', [
        'canView' => Route::has('subscribed'),
        'coursesData' => data
    ]);
})->name('courses');

This will render the Courses.vue component
from the '~/resources/js/Pages/' directory

Routing

/* ~/routes/web.php */

Route::get('/courses', [CoursesController::class, 'index'])->name('courses');

We can also use controllers

/* ~/app/Http/Controllers/CoursesController.php */

use Inertia\Inertia;

public function index() {

  return Inertia::render('Courses', [
          'canView' => Route::has('subscribed'),
          'coursesData' => data
          ]);

}

Authentication

You get to keep you server-side Laravel authentication system 😎

Server-Side Rendeirng

Inertia supports Server-Side Rendering

Inertia supports Server-Side Rendering

Can easily setup Inertia with SSR with Laravel Breeze and JetStream

php artisan breeze:install react --ssr
php artisan breeze:install vue --ssr

Server-Side Rendeirng

Sharing Data

use Inertia\Inertia;

// Synchronously...
Inertia::share('appName', config('app.name'));

The Inertia::share feature allows for global data sharing. This means that the specified data will be included in every request's response.

use Inertia\Inertia;
use App\Models\Cart;

// Lazy...
Inertia::share('shoppingCart', fn () => auth()->user()
        ? Cart::where('user_id', auth()->user()->id)->get()
        : null
);

Accessing Shared Data

Shared data will be available in usePage().props

// useShoppingCart() composable
import { computed } from "vue";
import { usePage } from "@inertiajs/vue3";

export function useShoppingCart() {
    const page = usePage();
    return computed(() => page.props.shoppingCart);
}
<script setup>
import { useShoppingCart } from "../Composables/useUser";

const shoppingCart = useShoppingCart();
</script>

<template>
  <ul>
	<li v-for="cartItem in shoppingCart.items">{{ cartItem.name }}</li>
  </ul>
</template>

SEO - <Head>

Inertia provides helper component for the page meta

<Head>
  <title>VueSchool</title>
  <meta name="description" content="Awesome learning experience">
</Head>

<Link>

<Link href="/logout" 
      method="post" 
      as="button" 
      type="button"
      >
  Logout
</Link>

// Renders as...
<button type="button">Logout</button>

Can be displayed as any HTML element using the as attribute

<Link>

<Link href="/logout" 
      method="post" 
      as="button" 
      type="button"
      >
  Logout
</Link>

// Renders as...
<button type="button">Logout</button>

Can send POST/PUT/PATCH/DELETE requests using the method attribute 

<Link>

<Link href="/endpoint" 
      method="post" 
      :data="{ foo: bar }"
      >
  Save
</Link>

When making POST or PUT requests, you can add additional data to the request using the data attribute.

<Link>

<Link href="/endpoint" 
      method="post" 
      :data="{ foo: bar }"
      :headers="{ foo: bar }"
      >
  Save
</Link>

The headers attribute allows you to add custom headers to an Inertia link

Forms

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
  email: null,
})
</script>

<template>
  <form @submit.prevent="form.post('/newsletter')">
    
    <input type="text" v-model="form.email">
    <div v-if="form.errors.email">{{ form.errors.email }}</div>
    
    <button type="submit" :disabled="form.processing">Subscribe</button>
    
  </form>
</template>

Inertia includes a form helper designed to help reduce the amount of boilerplate code needed for handling typical form submissions.

Forms

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
  email: null,
})
</script>

<template>
  <form @submit.prevent="form.post('/newsletter')">
    
    <input type="text" v-model="form.email">
    <div v-if="form.errors.email">{{ form.errors.email }}</div>
    
    <button type="submit" :disabled="form.processing">Subscribe</button>
    
  </form>
</template>

To submit the form, you may use the get, post, put, patch and delete methods.

Validation

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
  email: null,
})
</script>

<template>
  <form @submit.prevent="form.post('/newsletter')">
    
    <input type="text" v-model="form.email">
    <div v-if="form.errors.email">{{ form.errors.email }}</div>
    
    <button type="submit" :disabled="form.processing">Subscribe</button>
    
  </form>
</template>

If there are form validation errors, they are available via the errors property. 

Remembering State

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm('uniqueId', {
  email: null,
})
</script>

By providing a unique ID to the useForm helper,
the form data and errors will automatically be remembered
if the user navigates away

CSRF protection

When using Inertia with Laravel, you get CSRF protection out of the box. no additional configuration is required

File Upload

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
  avatar: null,
})
</script>

<template>
  <form @submit.prevent="form.post('/avatars')">
    
    <input type="file" @input="form.avatar = $event.target.files[0]" />
    
    <progress v-if="form.progress" :value="form.progress.percentage" max="100">
      {{ form.progress.percentage }}%
    </progress>
    
    <button type="submit">Submit</button>
  </form>
</template>

When making Inertia requests that include files, Inertia will automatically convert the request data into a FormData object.

File Upload

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
  avatar: null,
})
</script>

<template>
  <form @submit.prevent="form.post('/avatars')">
    
    <input type="file" @input="form.avatar = $event.target.files[0]" />
    
    <progress v-if="form.progress" :value="form.progress.percentage" max="100">
      {{ form.progress.percentage }}%
    </progress>
    
    <button type="submit">Submit</button>
  </form>
</template>

The form helper provides easy access to the current upload progress

Partial Reloads

import { router } from '@inertiajs/vue3'

router.reload({ only: ['courses'] })

When re-requesting the same page, you can selectively reload specific properties without altering surrounding data.

Partial Reloads

import { router } from '@inertiajs/vue3'

router.reload({ only: ['courses'] })

In this example, only the 'courses' prop will be updated when the page is reloaded.

This is thanks to the 'only' property.

Partial Reloads

import { router } from '@inertiajs/vue3'

router.visit('/endpoint', {
  only: ['courses'],
})

You can also do partial visits to other urls and only update specific page props.

Partial Reloads

import { Link } from '@inertiajs/vue3'

<Link href="/courses?free=true" :only="['courses']">Show Free Courses</Link>

The 'only' option is also available as a prop to the <Link> component.

Inertia, Vue, and Laravel collaborated seamlessly, enabling the creation of single-page, dynamic, server-side driven apps.

It still got so many more features to explore

inertiajs.com

But, it's not the only way to do it

But before I go!

vue.school/masterclass

Grab Some Swag 🤗

Mostafa Said

Thanks

@Moose_Said

For developing much of this talk

danielkelly_io

I'll tweet out the slides after this

Thank you 🙏

Go code awesome things!

(with Vue and Laravel 😉)

Vue, Inertia and Laravel

By Daniel Kelly

Vue, Inertia and Laravel

  • 156