@mosaid.bsky.social

🦋

@Moose_Said

- Director of Education @BitterBrains

- Web Development Educator

- Full Stack developer

M

ostafa

Vue.js Beyond

Frontend

the

Discover              and

Laravel

Inertia.js

The Beginning

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Why use Laravel with Vue?

Without needing deep PHP expertise

How to use Laravel with Vue?

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

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

Decoupled Vue.js and Laravel Integration

Option 2

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

Decoupled Nuxt.js and Laravel Integration

Option 3

The Laravel Backends for Vue.js 3 Course

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

Laravel Blade hosting Vue SFCs

Option 4

Each method, while effective, presented distinct challenges

  • Vue navigation overhead
  • Giving up Laravel routing and middleware
  • Laravel APIs for Vue
  • SSR Complexity
  • Global components bloat

Each method, while effective, presented distinct challenges

I just want to:

  • Use Vue.js for my frontend
  • Take full advantage of Laravel's features
  • Build a solid SPA (with SSR if needed)

But I don’t want to deal with creating APIs in Laravel

I just want to:

  • Use Vue.js for my frontend
  • Take full advantage of Laravel's features
  • Build a solid SPA (with SSR if needed)

Inertia steps in

And changes everything!

Inertia who?

Inertia is

Not a Framework

Inertia is

The Modern Monolith

First-party JavaScript and Laravel Library

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.
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>

Build single-page apps,

without

building an API.

But how is Inertia doing it?

https://vueschoo.io

Initial Visit

https://vueschoo.io

🌐

Server

Initial Visit

Request

https://vueschoo.io

🌐

Server

Initial Visit

HTML

Request

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

Request

Response

https://vueschoo.io

🌐

Server

Initial Visit

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

HTML

Client

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

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

Welcome To VueSchool

Inertia

Request

Response

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

Inertia

X-Inertia: true

Headers

https://vueschoo.io

Courses

👆

Client-Side Navigation

🌐

Server

XHR Request

JSON Response

JSON

X-Inertia: true

Headers

https://vueschoo.io

Courses

👆

Client-Side Navigation

🌐

Server

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.

JSON Response

X-Inertia: true

Headers

XHR Request

https://vueschoo.io

Client-Side Navigation

🌐

Server

JSON

{
  "component": "Courses",
  "props": {
    "coursesList": [{}]
  },
  "url": "/courses",
  "version": "version_number"
}
// Courses.vue
<script setup>
defineProps(['courses'])
</script>

<template>
  <h1> Courses </h1>
  <ul> <li v-for="course in courses"> course.name </li> </ul>
</template>

Inertia

Courses

XHR Request

JSON Response

X-Inertia: true

Headers

<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>
  • 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

and Vue developers who uses Laravel

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

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

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

Routing

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

Authentication

Inertia supports Server-Side Rendering

The returned HTML contains the actual content

<!DOCTYPE html>
<html>
  <head> .. </head>

  <body>
  	<main> Actual content .. </main>
  </body>
</html>

Server-Side Rendering

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
);

Sharing 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/useShoppingCart";

const shoppingCart = useShoppingCart();
</script>

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

Accessing Shared Data

Inertia provides helper component for the page meta

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

SEO - <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>

<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

<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">
    <p v-if="form.errors.email">{{ form.errors.email }}</p>
    
    <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

It supports GET, POST, PUT, PATCH, and DELETE methods for

handling various request types.

Forms: Request

<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">
    <p v-if="form.errors.email">{{ form.errors.email }}</p>
    
    <button type="submit" :disabled="form.processing">Subscribe</button>
    
  </form>
</template>

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

Forms: 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">
    <p v-if="form.errors.email">{{ form.errors.email }}</p>
    
    <button type="submit" :disabled="form.processing">Subscribe</button>
    
  </form>
</template>

It can track the form's processing state

Forms: Processing

<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">
    <p v-if="form.errors.email">{{ form.errors.email }}</p>
    
    <button type="submit" :disabled="form.processing">Subscribe</button>
    
  </form>
</template>
<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

Forms: Remembering State

When using Inertia with Laravel, you get CSRF protection out of the box.

No additional configuration is required

Forms: CSRF Protection

<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.

Forms: File Upload

The form helper provides easy access to the current upload progress

Forms: Upload Progress

<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>
import { router } from '@inertiajs/vue3'

const filterCourses = () => {
  // fetch and filter
  router.reload({ only: ['courses'] })
}

When re-requesting the same page,

you can selectively reload specific properties without altering

surrounding data.

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.

Partial Reloads

Inertia, Vue, and Laravel work smoothly together to build dynamic, server-driven single-page apps.

It still got so many more features to explore

inertiajs.com

Polling: Auto-refresh data at intervals for real-time updates.

Prefetching: Fetch data in advance for instant responses.

Deferred Props: Load essential data first for faster UI.

Infinite Scrolling: Load content as users scroll.

Lazy Loading: Load data only when needed.

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

// UserProfile.vue
<php>
  // Define a prop in PHP
  $name = prop(Auth::user()->name);
</php>

<template>
  <!-- Use it in Vue! -->
  Hello {{ name }}!
</template>

Crazy stuff are waiting ahead!

// UserProfile.vue
<php>
  // Define a prop in PHP
  $name = prop(Auth::user()->name);
</php>

<template>
  <!-- Use it in Vue! -->
  Hello {{ name }}!
</template>

Laravel Fusion

By: Aaron Francis

github.com/fusion-php/fusion

But before I go!

aidd.io

AI-Driven Development

Grab Some Swag 🤗

I'll post out the slides after this

@Moose_Said

@mosaid.bsky.social

🦋

Thank you 🙏

Vue.js Beyond the Frontend: Discover Laravel and Inertia.js

By Mostafa Said

Vue.js Beyond the Frontend: Discover Laravel and Inertia.js

  • 138