Enhancing Laravel and Inertia.js with TypeScript and Vue 3's Composition API to build a Powerful SPA
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.
driesvints liked this article
Other articles you might like
Laravel Custom Query Builders Over Scopes
Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...
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....
🍣 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...
The Laravel portal for problem solving, knowledge sharing and community building.
The community