Support the ongoing development of Laravel.io →

Enhancing Laravel and Inertia.js with TypeScript and Vue 3's Composition API to build a Powerful SPA

6 Nov, 2023 5 min read

Photo by Ricardo Gomez Angel on Unsplash

Introduction

Laravel, when combined with Inertia.js, offers a powerful duo for creating modern single-page applications that rival the complexity of full frontend frameworks using Vue.js or React. This article guides you through the process of integrating Laravel with Inertia.js and Vue 3, then goes a step further by introducing TypeScript within Vue's Composition API.

Prerequisites

  • Composer installed on your local machine
  • Node.js and npm/yarn installed
  • Basic knowledge of Laravel, Vue.js, and TypeScript

Setting Up Laravel with Inertia.js

Create a new Laravel project and navigate to your project directory:

composer create-project laravel/laravel inertia-example
cd inertia-example

Install Inertia.js with Composer:

composer require inertiajs/inertia-laravel

And publish the Inertia.js configuration:

php artisan inertia:middleware

Make sure to register the Inertia middleware by adding it at the end of the web middleware group in app/Http/Kernel.php.

Integrating Vue 3 and TypeScript

With Laravel set up, let's shift focus to the frontend. Install Vue 3 and Inertia.js's Vue adapter:

npm install vue @inertiajs/vue3 @types/ziggy-js lodash --save-dev

Add TypeScript and its Vue plugin:

npm install typescript @vitejs/plugin-vue vue-tsc --save-dev

Set up your tsconfig.json for TypeScript:

{  
    "compilerOptions":{  
        "allowJs":true,  
        "module":"ESNext",  
        "moduleResolution":"Node",  
        "target":"esnext",  
        "jsx":"preserve",  
        "strict":true,  
        "esModuleInterop":true,  
        "skipLibCheck":true,  
        "forceConsistentCasingInFileNames":true,  
        "noEmit":true,  
        "isolatedModules":true,  
        "types": ["vite/client"]  
    },    
    "include":[  
        "resources/js/**/*.ts",  
        "resources/js/**/*.d.ts",  
        "resources/js/**/*.vue"  
    ]  
}

Let's now create the Typescript types required:

Head to resources/js and create a directory named types, within the directory create a TS file named global.d.ts and copy the following content in the file:

import { PageProps as InertiaPageProps } from '@inertiajs/core';
import { AxiosInstance } from 'axios';
import ziggyRoute, { Config as ZiggyConfig } from 'ziggy-js';
import { PageProps as AppPageProps } from './';

declare global {
    interface Window {
        axios: AxiosInstance;
    }

    var route: typeof ziggyRoute;
    var Ziggy: ZiggyConfig;
}

declare module 'vue' {
    interface ComponentCustomProperties {
        route: typeof ziggyRoute;
    }
}

declare module '@inertiajs/core' {
    interface PageProps extends InertiaPageProps, AppPageProps {}
}

Install Ziggy for route handling in Vue

Ziggy is a package developed by Tighten for Laravel in order to share the routes from routes/web.php with the client side.

composer require tightenco/ziggy

Updating the Frontend Build Tools

Configure Vite to work with TypeScript by adjusting your vite.config.js:

import { defineConfig } from 'vite';  
import laravel from 'laravel-vite-plugin';  
import vue from '@vitejs/plugin-vue';  
import path from 'path';  
  
  
export default defineConfig({  
    "plugins":[  
        laravel({  
            "input": [  
                "resources/css/app.css",  
                "resources/js/app.ts"  
            ],  
            "refresh": true  
        }),  
        vue({  
            template: {  
                transformAssetUrls: {  
                    base: null,  
                    includeAbsolute: false,  
                },            
			},       
		 })    
	 ],
 });

Modify package.json to include scripts for TypeScript compilation and Vite's build process:

"scripts": {  
    "dev": "vite",  
    "build": "vue-tsc --noEmit && vite build",  
    "ts-check": "vue-tsc --noEmit"  
},

Update your entry point to TypeScript in resources/js/app.ts and make sure to change the extension of resources/js/app.js to app.ts as well as for bootstrap.js to bootstrap.ts.

import './bootstrap';  
import '../css/app.css';  
  
import { createApp, h, DefineComponent } from 'vue';  
import { createInertiaApp } from '@inertiajs/vue3';  
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';  
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.es';  
  
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';  
  
createInertiaApp({  
    title: (title) => `${title} - ${appName}`,  
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob<DefineComponent>('./Pages/**/*.vue')),  
    setup({ el, App, props, plugin }) {  
        createApp({ render: () => h(App, props) })  
            .use(plugin)  
            .use(ZiggyVue, Ziggy)  
            .mount(el);  
    },    progress: {  
        color: '#4B5563',  
    },});  

Defining Routes and Views

Create the layout file in resources/views/app.blade.php:

<!DOCTYPE html>  
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="h-full bg-gray-100">  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport" content="width=device-width, initial-scale=1">  
  
    <title inertia>{{ config('app.name', 'Laravel') }}</title>  
  
    <!-- Fonts -->  
    <link rel="preconnect" href="https://fonts.bunny.net">  
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />  
  
    <!-- Scripts -->  
    @routes  
    @vite(['resources/js/app.ts', "resources/js/Pages/{$page['component']}.vue"])  
    @inertiaHead  
</head>  
<body class="font-sans antialiased h-full">  
@inertia  
</body>  
</html>

Define your Inertia routes in routes/web.php:

Route::get('/', function () {  
    return Inertia\Inertia::render('Welcome');  
});  
  
Route::get('/about', function () {  
    return Inertia\Inertia::render('About');  
});

Make sure you create the corresponding Vue components in the resources/js/Pages directory.

Using TypeScript with Vue's Composition API

To leverage TypeScript within your Vue components, use the <script setup lang="ts"> syntax, which simplifies the Composition API usage.

Example resources/js/Pages/Welcome.vue:

<script setup lang="ts">  
import { ref } from 'vue';  
import { Link } from '@inertiajs/vue3';  
  
const greeting = ref('Welcome to Your Vue.js + TypeScript App');  
  
</script>

<template>  
    <h1>{{ greeting }}</h1>  
    <Link href="/about">Go to About page</Link>  
</template>

And another one for About.vue:

<template>  
    <h1>{{ greeting }}</h1>  
    <Link href="/">Go to Welcome page</Link>  
</template>  
  
<script setup lang="ts">  
import { ref } from 'vue';  
import { Link } from '@inertiajs/vue3';  
  
const greeting = ref('Welcome to About page');  
  
</script>

Conclusion

Integrating TypeScript with Laravel, Inertia.js, and Vue 3's Composition API may seem daunting at first, but it's a worthwhile investment for development efficiency and performance gains. With this setup, you get the type-checking prowess of TypeScript, the reactivity and composability of Vue 3, and the seamless interactivity provided by Inertia.js, all within a robust Laravel application.

For further exploration, consider delving into advanced TypeScript features, state management with Vuex or Pinia, and automatic type generation for Inertia's route linking.

Last updated 2 weeks ago.

driesvints liked this article

1
Like this article? Let the author know and give them a clap!
bcryp7 (Alberto Rosas) Hi, I'm Alberto. As a Senior Software Engineer and Consultant.

Other articles you might like

November 18th 2024

Laravel Custom Query Builders Over Scopes

Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...

Read article
November 19th 2024

Access Laravel before and after running Pest tests

How to access the Laravel ecosystem by simulating the beforeAll and afterAll methods in a Pest test....

Read article
November 11th 2024

🍣 Sushi — Your Eloquent model driver for other data sources

In Laravel projects, we usually store data in databases, create tables, and run migrations. But not...

Read article

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.