Lead Instructor @ Vue School
Full Stack developer (10 years)
Husband and Father
The Laravel Backends for Vue.js 3 Course
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>
https://vueschoo.io
https://vueschoo.io
🌐
Server
Request
https://vueschoo.io
🌐
Server
Request
HTML
Response
https://vueschoo.io
🌐
Server
Request
<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
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
👆
https://vueschoo.io
Courses
👆
🌐
Server
XHR Request
X-Inertia: true
Headers
https://vueschoo.io
Courses
👆
🌐
Server
XHR Request
X-Inertia: true
Headers
Inertia
https://vueschoo.io
Courses
👆
🌐
Server
XHR Request
X-Inertia: true
Headers
JSON Response
JSON
https://vueschoo.io
Courses
👆
🌐
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
🌐
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
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
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
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
/* ~/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
]);
}
You get to keep you server-side Laravel authentication system 😎
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
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
);
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>
Inertia provides helper component for the page meta
<Head>
<title>VueSchool</title>
<meta name="description" content="Awesome learning experience">
</Head>
<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 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 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 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
<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.
<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.
<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.
<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
When using Inertia with Laravel, you get CSRF protection out of the box. no additional configuration is required
<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.
<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
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.
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.
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.
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
Mostafa Said
Thanks
@Moose_Said
For developing much of this talk
danielkelly_io
I'll tweet out the slides after this
Go code awesome things!
(with Vue and Laravel 😉)