Working with Laravel Jetstream VueJS 3 & InertiaJS with SSR Support

Mostafa Said - Jul 15 '22 - - Dev Community

You feel like you're describing a brand-new car with some fairly impressive characteristics when you read this article's headline, don't you? Working with such tools will undoubtedly give you the impression that you are driving a brand-new, stylish car.

In this Article, I'm going to help you get started with Laravel's JetStream InertiaJS stack with SSR support. JetStream will provide your next Laravel project with a well secured authentication system and InertiaJS with VueJS3 will give it a modern and elegant taste. I'll assume you have some familiarity with Laravel, but it's okay if you don't. Just follow along, and I'm sure you'll find this tutorial helpful.

I would like to start this by taking few seconds to give you a quick background about JetStream and InertiaJS.

What is JetStream:

Jetstream provides the implementation for your application's login, registration, email verification, two-factor authentication, session management, API via Laravel Sanctum, and optional team management features. Jetstream is designed using Tailwind CSS and offers your choice of Livewire or Inertia scaffolding. Source

It's the more complete version of Laravel Breeze authentication system. JetStream allow you to start your next application without wasting anytime creating a secure authentication mechanism for you app.

What is InertiaJS:

Inertia allows you to create fully client-side rendered, single-page apps, without much of the complexity that comes with modern SPAs. It does this by leveraging existing server-side frameworks. DOCS

InertiaJS works side by side with Laravel and VueJS. You can think of it as a friendly neighbor who is always trying to help you to do anything. Inertia also allow you to use Laravel router instead of Vue router.

Why should I use Inertia with SSR support:

Server-side rendering allows you to pre-render an initial page visit on the server, and to send the rendered HTML to the browser. This allows visitors to see and interact with your pages before they have fully loaded, and also provides other benefits such as decreasing the time it takes for search engines to index your site. DOCS

Search engine optimization (SEO) might be challenging when using VueJS since single page apps are known to confuse search engines. There are other solutions available to that problem, however InertiaJS came to the rescue by adding SSR (Server Side Rendering) functionality.

Time to get started 🚀

1- Install new Laravel App:

You're free to choose the best installation method, I will use for this tutorial Laravel Installer.

laravel new {project name}
Enter fullscreen mode Exit fullscreen mode

Don't forget to replace {project name} with any name you want to give your project.

2- Install JetStream with SSR support:

When you install JetStream and Vue Inertia stack, it will install InertiaJS with VueJS 3 and TailwindCSS by default. The installation will configure everything for you, you really won't have to do a thing.

  • Start with JetStream composer package
composer require laravel/jetstream
Enter fullscreen mode Exit fullscreen mode
  • Install InertiaJS stack with SSR support
php artisan jetstream:install inertia --ssr
Enter fullscreen mode Exit fullscreen mode

3- Start your Laravel server:

Since Laravel recently runs on Vite, we need to run 2 terminal commands.

npm run dev
Enter fullscreen mode Exit fullscreen mode

And in another terminal window we need to run the below:

php artisan serve
Enter fullscreen mode Exit fullscreen mode

And Voilà! You have a Laravel project with a fully secured authentication system, InertiaJS components styled with TailwindCSS and all of that ready to be customized to fit your project.

4- Laravel & Inertia Routing:

Let's create a Product model and allow our users to create a new product and get redirected to the newly created product page.

  • Create a new database and connect to use using your .env file in the main project directory. In my case I created a new mySQL database called tutorial and made the below changes in .env file.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tutorial
DB_USERNAME=root
DB_PASSWORD=
Enter fullscreen mode Exit fullscreen mode
  • Open up your terminal and hit the below command to create a Product model with migration:
php artisan make:model Product -mc
Enter fullscreen mode Exit fullscreen mode
  • Head to your products table in database/migration and make the below changes to add user_id, name and slug for each product.
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id');
            $table->string('name');
            $table->string('slug');
            $table->timestamps();
        });
    }
Enter fullscreen mode Exit fullscreen mode
  • Now go to your newly created app/models/Product file and make those columns fillable and write the product relation to users. Since every product will belong to a user, then we're going to use belongsTo relationship.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'name',
        'slug',
    ];


    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Let's add the User relationship which will be that every user has many products. We will just add the relationship to the User model without changing anything else.
    public function products()
    {
        return $this->hadMany(Product::class);
    }
Enter fullscreen mode Exit fullscreen mode
  • Migration time.
php artisan migrate:fresh
Enter fullscreen mode Exit fullscreen mode
  • Time to work our routes, we need one route to show the product and another to post the create product request. In this tutorial, we will use routes/web.php as our playground.

First thing, let's work our route to show the product.

//Route to show the product
Route::get('{slug}', function () {
       return Inertia::render('ProductShow')>name('products.show');
});
Enter fullscreen mode Exit fullscreen mode

Notice that we don't use return view() anymore, the return Inertia::render allows you to render InertiaJS components.

Now we need the other route for posting the request.


use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

//Route to submit a new product and redirect to show it
Route::post('products/create', function (Request $request) {
    $validated = $request->validate([
        'name' => 'required',
        'slug' => 'required',
    ]);

    $user_id = $validated['user_id'] = auth()->user()->id;
    $product_slug = $validated['slug'];

    Product::create($validated);

    return redirect()->route('products.show', [$user_id, $product_slug]);
})->name('products.create');
Enter fullscreen mode Exit fullscreen mode

5- InertiaJS Forms:

  • Notice that we already have welcome route set by default in web.php, we will use this welcome page to add to it our form.

  • Head to resources/js/Pages/Welcome.vue and replace it with the below code. We will use Inertia's useForm helper.

<script setup>
import { Head, Link } from "@inertiajs/inertia-vue3";
import { useForm } from "@inertiajs/inertia-vue3";

defineProps({
    canLogin: Boolean,
    canRegister: Boolean,
});

const form = useForm("createProduct", {
    name: null,
    slug: null,
});

const submit = () => {
    form.post(route("products.create"), {
        onSuccess: () => form.reset(),
    });
};
</script>

<template>
    <Head title="Welcome" />

    <div
        class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0"
    >
        <div
            v-if="canLogin"
            class="hidden fixed top-0 right-0 px-6 py-4 sm:block"
        >
            <Link
                v-if="$page.props.user"
                :href="route('dashboard')"
                class="text-sm text-gray-700 underline"
            >
                Dashboard
            </Link>

            <template v-else>
                <Link
                    :href="route('login')"
                    class="text-sm text-gray-700 underline"
                >
                    Log in
                </Link>

                <Link
                    v-if="canRegister"
                    :href="route('register')"
                    class="ml-4 text-sm text-gray-700 underline"
                >
                    Register
                </Link>
            </template>
        </div>

        <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
            <form @submit.prevent="submit">
                <input type="text" id="name" name="name" v-model="form.name" />
                <input type="text" id="slug" name="slug" v-model="form.slug" />
                <button type="submit">Submit</button>
            </form>
        </div>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Have a look at the above code and see how simple the syntax is for the form integration with Inertia's useForm helper. We make a constant called form and make it equal to useForm then we give a name to our form and define the data. then we make const for our submission and give it the Laravel route name and that's it!

  • Now create another file in the same directory and name it Product.vue and paste the below code.
<script setup>
import { Head } from "@inertiajs/inertia-vue3";
</script>

<template>
    <Head title="Product" />

    <div
        class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0"
    >
        <h1 class="text-6xl font-bold text-black">Hi, I'm product</h1>
    </div>
</template>

Enter fullscreen mode Exit fullscreen mode

Now you're ready to test your project 😎

register a new user and head to the homepage, fill the form and click on submit. You will find yourself magically redirected to the product.show route without page reloads or any kind of delay.

I hope that this quick tutorial was useful. Despite my current hectic schedule, I continue to do my best to give back to the community. Please post any questions you may have in the comments section, and I will get back to you as soon as I can 👋

. . . . . . . . . . . . . . . . . . . . . . . . .