In this lesson, we’ll learn about two different things. First, we’ll learn how to register a global component within our Vue 3 Inertia application. Since links are fairly common in any application we’ll be taking the Inertia Link component and registering it globally.
Then, we’ll learn how we can simplify our imports using Webpack Encore and path aliases. This will allow us to use a single reference point when importing client-side files inside our Vue 3 application instead of relying on relative paths.
Global Components
In order to register a global component, we’ll want to jump inside our client-side app.js
file at resources/js/app.js
. At the top of the file let’s add our import, in this case we’ll import the Inertia Link component as shown below.
import '../css/app.css'
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
import DefaultLayout from './Layouts/Default.vue'
import { Link } from '@inertiajs/inertia-vue3' // 👈 import the Link component
createInertiaApp({ ... })
If you recall back to a few lessons ago, you’ll remember createApp
and the methods chained off of it are the portions of the below actually creating our Vue 3 application, everything else is Inertia-based.
So, all we need to do to register a global Vue 3 component is chain off the createApp
function the component
method. Just note, you’ll want to call component
before mount
is called.
import { Link } from '@inertiajs/inertia-vue3'
createInertiaApp({
resolve: name => { ... },
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.component('inertia-link', Link) // 👈
.mount(el)
},
})
The first argument of the component
method is the name the component will be registered to. The second argument is for the component itself. Once that’s set, the component is now globally registered and we can now replace all our links with our globally registered inertia-link
component.
Using Our Global InertiaLink Component
First, let’s jump into our home page and update the Link component there to our globally registered inertia-link
.
<template>
<!-- resources/js/Pages/Home.vue -->
<div>
<!-- <Link href="/login">Login</Link> -->
<inertia-link href="/login">Login</inertia-link>
<h1 class="text-red-500">Testing, {{ testing }}</h1>
<n-button>Testing</n-button>
<n-input :value="testing"></n-input>
</div>
</template>
<script>
// import { Link } from '@inertiajs/inertia-vue3'
export default {
props: {
testing: String
},
// components: {
// Link,
// }
}
</script>
As you can see above, all we need to do is remove our Link
import and component registration from our component. Then swap it out within the template
with our inertia-link
component.
Let’s do the same on our Login page.
<template>
<!-- resources/js/Pages/Auth/Login.vue -->
<div>
<!-- <Link href="/home">Home</Link> -->
<inertia-link href="/home">Home</inertia-link>
<label>Login</label>
<a href="/home">Anchor Home</a>
</div>
</template>
<script>
import AuthLayout from '../../Layouts/Auth.vue'
export default {
layout: AuthLayout
}
</script>
<!--
<script setup>
import { Link } from '@inertiajs/inertia-vue3'
</script>
>
In this case, since we’re using a setup script with nothing else going on, we can just remove the setup script and swap out the component in the template.
Simplifying Imports with Path Aliases
Next, let’s get our path alias set up so that we don’t need to rely on relative paths to import components, but instead can reference our alias.
For example an import like this:
import AuthLayout from '../../Layouts/Auth.vue'
Can be simplified to the below using a path alias.
import AuthLayout from '@/Layouts/Auth.vue'
This has a couple of benefits.
First, when we go to import a file, we won’t need to stop and think about the depth level we’re currently in nor the depth level the component we need is in.
Second, if we were to copy/paste the import using the relative path we’d need to update the path if our nested level changed at all in comparison to the imported file. Using a path alias we can copy/paste without needing to update our path regardless of where we paste the import too.
Add the Path Alias
First things first, if you have your server running, go ahead and stop it as we’ll need to update our webpack.config.js
to add the alias. Then, go ahead and open your webpack.config.js
file.
At the top, go ahead and add an import for resolve
from the path
package.
const { join, resolve } = require('path') // 👈 add import for resolve
const Encore = require('@symfony/webpack-encore')
const Components = require('unplugin-vue-components/webpack')
const { NaiveUiResolver } = require('unplugin-vue-components/resolvers')
Then, scroll on down to where we have our Vue Loader config, around line 186
. Here we’ll add our alias using the Encore.addAliases
method.
Encore.enableVueLoader(() => {}, {
version: 3,
runtimeCompilerBuild: false,
useJsx: false
})
Encore.addPlugin(Components({
resolvers: [NaiveUiResolver()]
}))
// 👇
Encore.addAliases({
'@': resolve(__dirname, 'resources/js')
})
This method accepts an object. The key of the object is the aliased character, which we’ll use '@'
here. The second is the path we want this alias to reference. The best place for this will be our resources/js
directory as this will allow us to reach for our pages, layouts, and components easily within our Vue application.
At this point, you can go ahead and restart your server if you wish.
Using Our Alias
Lastly, let’s go ahead and make use of our new alias! So, jump back into your Login page, and let’s replace the AuthLayout
import from ../../Layouts/Auth.vue
to @/Layouts/Auth.vue
.
<template>
<!-- resources/js/Pages/Auth/Login.vue -->
<div>
<inertia-link href="/home">Home</inertia-link>
<label>Login</label>
<a href="/home">Anchor Home</a>
</div>
</template>
<script>
import AuthLayout from '@/Layouts/Auth.vue' // 👈
export default {
layout: AuthLayout
}
</script>
Cleaning Up Our Project
Before we end this project, let’s go ahead and do some cleanup within our application to get ready for the next set of lessons. Most of what we have in our project so far is testing to ensure everything is set up correctly, so let’s go ahead and clean all that out.
Let’s start by creating a header so we can easily navigate from page to page.
Creating Our Header
Let’s create it at resources/js/components/Header.vue
<template>
<!-- resources/js/components/Header.vue -->
<div class="bg-slate-100 py-3 px-6 flex justify-between items-center mb-6">
<div class="flex items-center space-x-6">
<h3 class="font-bold">AdonisJS InertiaJS Example</h3>
<nav class="flex items-center text-sm">
<inertia-link href="/app">Home</inertia-link>
</nav>
</div>
<div class="flex items-center space-x-6 text-sm">
<inertia-link href="/app/login">Login</inertia-link>
<inertia-link href="/app/register">Register</inertia-link>
</div>
</div>
</template>
Here we have three inertia-link
usages one for our home page, one for our login page, and a last for our register page that we’ll create a little later on. You may also notice all three href
values are prefixed with /app
.
Updating Our Routes
We’ll keep everything Inertia-based under the /app
path prefix and everything Edge-based outside of /app
. So, let’s jump into our routes and update it to the below.
// start/routes.ts
Route.get('/', async ({ view }) => {
return view.render('welcome')
})
Route.group(() => {
Route.get('/', async ({ inertia }) => {
return inertia.render('App', {
testing: 'this is a test'
})
})
Route.get('/login', async ({ inertia }) => {
return inertia.render('Auth/Login')
})
Route.get('/register', async ({ inertia }) => {
return inertia.render('Auth/Register')
})
}).prefix('app')
We’re renaming home
to app
. You’ll also want to update the Home.vue
filename to App.vue
as well. Then, we’ll wrap our app, login, and register routes inside a group that’s prefixed with app
. Lastly, we’re adding a route for our register route. We’ll make some changes to our login page in a moment, then copy/paste that file to create our register page.
Applying Our Header To Our Layouts
In addition to applying our header to our layouts, we’ll also replace the placeholder text we have stating what layout we’re in. Let’s also wrap our slotted content in a px-6
to match the padding within our header.
<template>
<!-- resources/js/Layouts/Default.vue -->
<div>
<Header />
<main class="px-6">
<slot></slot>
</main>
</div>
</template>
<script setup>
import Header from '@/components/Header.vue'
</script>
And the same for our AuthLayout
. At this point our AuthLayout
will match our DefaultLayout
, but we’ll change that in one of the upcoming lessons so we’ll keep them separated.
<template>
<!-- resources/js/Layouts/Auth.vue -->
<div>
<Header />
<main class="px-6">
<slot></slot>
</main>
</div>
</template>
<script setup>
import Header from '@/components/Header.vue'
</script>
Cleaning Our Pages
Next, let’s clean out our pages of the “test” elements and components we’re using.
First our App page.
<template>
<!-- resources/js/Pages/App.vue -->
<div>
<h1>Welcome</h1>
</div>
</template>
<script>
export default {
props: {
testing: String
},
}
</script>
Then, our Login page.
<template>
<!-- resources/js/Pages/Auth/Login.vue -->
<div>
<h1>Login</h1>
</div>
</template>
<script>
import AuthLayout from '@/Layouts/Auth.vue'
export default {
layout: AuthLayout
}
</script>
Lastly, let’s copy/paste our Login page and rename the pasted version to Register.vue
to serve as our registration page. Then, update the h1
for that page to Register.
<template>
<!-- resources/js/Pages/Auth/Register.vue -->
<div>
<h1>Register</h1>
</div>
</template>
<script>
import AuthLayout from '@/Layouts/Auth.vue'
export default {
layout: AuthLayout
}
</script>
Next Up
That should do it for now! In the next lesson, we’ll build off the clean-up we just did by learning about Inertia and how forms work within it by working on our registration form.
Join The Discussion! (6 Comments)
Please sign in or sign up for free to join in on the dicussion.
fearless
Hi Tom,
Thank you for sharing this awesome guide with us.
What would be solution for rendering assets (images) inside Vue component using
AdonisJS assets-manager, similar to asset helper?
Thanks.
Please sign in or sign up for free to reply
tomgobich
Hi fearless!
Thanks for watching/reading!
You should be able to just plop the images you need inside the public directory, something like
/public/images/yourawesomeimage.jpg
, then just drop/public
to use it inside your Vue components.Please sign in or sign up for free to reply
Jean
Hi.
Do you know how to get rid of the following warning with Volar and the <script> tag in vue files : `Virtual script not found, may missing <script lang="ts"> / "allowJs": true / jsconfig.json.`.
If I want to use TypeScript by adding `lang="ts"` I have a compile error.
I noticed you also had this warning in previous videos and now you don't.
Great content as always sir.
Please sign in or sign up for free to reply
tomgobich
Hi Jean! I unfortunately am not sure. I believe I'm still using Vetur instead of Volar for Vue in VSCode. Maybe Vetur updated and patched this?
Please sign in or sign up for free to reply
Jean
Vetur is still being updated?
I don't have autocompletion either. It's frustrating.
If I put "allowJs: true" in tsconfig.json like suggested, it works but I feel like this is not the best solution.
Edit : I found another solution. Add an empty jsconfig.json file inside js directory. What do you think ?
Please sign in or sign up for free to reply
tomgobich
I'm honestly not sure whether Vetur is still being supported. I know the consensus from the core team was that Volar is the preference going forward, but I just haven't switched yet as Vetur is working fine for me.
Yeah - if adding an empty jsconfig.json is working okay for you that may be a valid solution. I know there was an instance where I needed to add a separate tsconfig.json to get things working as the Adonis namespaces conflicted with the local front-end TypeScript.
Please sign in or sign up for free to reply