Laravel, Vue and Inertia with Vite
Webpack is great… but there might be better.
But first… some shameless self promotion
Vite is a fantastic piece of software and if you’re keen to see how I got it working with Laravel and Inertia, then read on. However, if you just want it to work, then I’ve written a simple Laravel package that will “boiletplate” this all out for you. It’s called “Vitamin” (clever, I know). I’ve managed to work out how to get around a lot of the stumbling blocks so it’s a fairly safe solution to all this, and will get you there in just a few minutes. Check it out here.
Also, a shout out
When I started experimenting with Vite, This article by Sebastian De Deyne got me 90% of the way. There were a few things that didn’t work for me and I had to figured out a bunch, but you may notice some similarities. There’s also a laravel-vite package that’s worth looking at.
Vite is still quite new and there’s isn’t too much documentation or tooling around its use with Laravel out there. But it’s a great tool and it works really well with things I already use. Here’s hoping you find this post useful as well.
Into the fray…
I’m a big Laravel fan. Not only has it helped me become a better programmer, but it’s now also a central component of my own business. I can’t really see myself giving up Laravel any time in the near future.
Laravel comes with its own templating language called Blade. Blade is great and I’ve built many a project with it. At some point I also started using Vue to sprinkle a little interactive magic into those blade files. It’s a nice way to go, but I felt like there were other solutions. Don’t get me wrong, Blade is great and I still use it plenty, I was just interested in learning something new.
Over the years I’ve also built a few single-page apps using Laravel and Vue, but I’ve never really been a big fan of that path. Vue-router and Vuex are really great tools, but making it all work with Laravel just felt awkward. So when Johnathan Reinink wrote his first blog post on what would eventually become Inertia, I was hooked.
At the time, I worked for a small web developement company, and as lead developer I had a lot of sway over our tech choices, so being the tech bully I can sometimes be, I managed to get us to buld a single project using the ideas that Johnathan spoke about. It was like a breath of fresh air. We got all the magic of Laravel but could build our front-end completely in Vue single-file components. When Inertia was eventually released it was a no-brainer. It made perfect sense for me.
So over the last little while, my tech stack has been almost exclusively Laravel, Vue, and Inertia. This all works because Webpack and Laravel Mix are great. There’s also not really a lot of initial set up needed. Laravel even comes with a “one-click install” for this setup these days.
However, There’s a big shortfall here: It’s slow. It works wonders when your project is small and has a fairly simple UI, but as soon as it starts getting a bit more complex, things slow down. This is because each time you make a change, your entire front-end gets re-bundled. This means that you could end up waiting upto 15 seconds each time you make a change, and that’s just not cool.
I figured I needed some way to speed things up. As a big Vue fan, I’d already heard about Vite, but didn’t really give it much thought at the time because I was happy with my set up. I didn’t think I needed to change anything. Well, after getting frustrated while working on a very large front-end project, I decided it was time to take another look. The speed improvement is insane. My productivity has improved tenfold. I spend zero time waiting for things to bundle and my old 2017 Macbook CPU is really grateful. I don’t hear that aweful fan nearly as much now.
Vite is fast because it leverages a fairly modern browser feature called ES modules. All modern browsers support modules now so your front-end components get injected into the browser each time you make a change. No need to bundle all your components together, so no waiting. Changes are close to instant.
Setting up Vite with Laravel takes a little work, but it really is worth the effort. There’s a few articles out there that describe getting Laravel and Vite working, and the best one I found was from Sebastian De Deyne (link above).
I’ve been building on Sebastian’s idea and I think I now have a Vite based build pipeline that suites me well. So this article will describe the initial setup and some of the pitfalls I almost got caught up in.
Before you begin
It’s important to note that this is fairly opinionated. It works well for me, but there are some caveats. It’s up to you what you’re willing to put up with and remember Webpack has been around a lot longer than Vite. If you’re uncomfortable using newer tools, then this might not be for you.
I’ve also found that some changes don’t get inserted into the browser automatically. making changes to your template in a Vue component works fine, but adding a child component, or making changes to any scripts require a browser reload. This is because of how Vue components are booted. I don’t mind reloading my browser every so often.
This is a Laravel approach to Vite, so obviously you’ll need a Laravel app. I’ve had success moving an older Laravel Mix app over, so you can do it on something that already exists, but to make these first attempts easier I’d suggest starting with a fresh app. It’s also important to note that I’m a Laravel Valet user and I wanted to continue using that.
Once you have a new Laravel app up and running and you can get to it through your browser, you’re ready to go.
First, you can remove all the bits you don’t need anymore. You’re not using Webpack or Laravel Mix anymore, so you can remove any associated files. You can also remove some of the default Laravel stuff:
You’ll also want to remove the Laravel Mix dependency from package.json.
Next, you’ll need to install dependencies for Inertia, Vite, Vue and Tailwind. (You don’t need to use Tailwind if that’s not your thing, but it’s my go-to now days, so I’ve included it here). The only PHP dependency you’ll need forr now is Inertia, so:
composer require inertiajs/inertia-laravel
Then the Node dependencies for Inertia, Vue, Vite and Tailwind:
yarn add @inertiajs/inertia @inertiajs/inertia-vue3 vue@next vite @vitejs/plugin-vue @vue/compiler-sfc autoprefixer@latest postcss@latest tailwindcss@latest --dev
Now let’s get the Tailwind, Inertia and Vue configuration bits out of the way. Tailwind is fairly simple:
npx tailwindcss init -p
In the newly created
tailwind.config.js change the
purge array to:
I’d also recommend adding
mode: 'jit', to that config.
You can’t use the
require method anymore because that’s a Webpack implementation. Instead, you need to use
import. You can then use the
If you’re just starting out with Inertia, one of the things you’ll miss is Laravel’s super handy `route` helper function. If that sounds like you then I cannot recommend Ziggy highly enough. It takes all your Laravel routes and makes them available to your front end. It then gives you a similar `route` helper function. I use it in nearly every Laravel project.
Vite is configured using a
vite.config.js file placed at the root of your project. TypeScript is supported out of the box, so you could name the file
vite.config.ts. I’ve only recently started with TypeScript and still deciding if it’s all worth it. I’m not going that route for now, but if I feel more confident in the future then I’ll update this with some info. If you’re a TypeScript pro, then I’m sure you’ll manage to make the necessary changes.
Setting up Vite is fairly simple. If you have any experience configuring Webpack, this will seem like a breath of fresh air. Here’s a basic
vite.config.js configuration file to start with:
Vite will inject the command that is run into the config file so you can use that information to change the configuration depending on the environment you’re currently working in. This is really handy. The
command parameter will be either
build depending on how you run vite from the command-line, which would normally look something like this:
npx vite build// ornpx vite serve
You’ll use this
command parameter soon.
By default, Vite assumes that the base directory is the public path your assets are served from. You might be used to having a
css directory, but I’d suggest using a single
build directory for all your assets. Doing so makes it easier to configure and it’s easier to just ignore a whole directory when you eventually commit this to your repo. You can tell Vite to do this by setting the
base config option:
The catch here is that this is only for production. During development, you don’t need this base directory because Vite will serve your unbundled assets through its own server. This is where that
command parameter comes into play:
Now when you run Vite in development, it will spin up a web server to serve development assets and wont use the
build path. But when bundling for production, the
build path will be used when generating a manifest (we’ll get to this soon).
The next setting we need to look at is
publicDir. As the Vite documentation states, this is the directory that Vite will serve at
/ during dev. When building for production, assets in this directory will be copied to
outDir (which we’ll get to next). Since we already have a public directory, we don’t really need Vite to copy anything, so we can set this to
That’s it for the development config, now we just need to add some stuff for when you run
build. Vite’s build config is all under the
build key and there’s a few things you’ll want to set here. If you’re familiar with Laravel Mix, then you’ll know what the
mix-manifest.json file is for. You can get Vite to do the same thing by setting `manifest` to `true`:
We also need to tell Vite where to place the bundled assets. We can do so using the `outDir` option:
So now you’ve told Vite that the base path for all assets is
/build/ which will be used when you bundle assets for production. So if your app will eventually be at
https://my.app/build will be where you’ll find all the bundled assets. You’ve also told it that when building for production, to place everything in the
input Rollup config option and passing it to
That’s a good start. If you run
npx vite serve now, you’ll get something like this:
You should now have a dev server running at https://localhost:3000/. If you run
npx vite build, Vite will bundle your assets ands you should get a
public/build directory with your bundled assets. Remember that Vite doesn’t know anything about your Laravel app, and it shouldn’t. It’s only concerned with serving and bundling front-end assets.
I like Valet. I work on a bunch of different projects on a daily bais, so Valet makes that a little simpler. There might be better solutions here, but Valet feels natural to me. To make this work with Valet, you need to tell Vite what host you’re using otherwise you’ll come up agaist a whole bunch of CORS errors.
You can do this by adding a
--host option when running Vite:
npx vite serve --host=mysite.test
Working this way leads to a small problem. Using the FontSource library, I found that Vite would not be able to find the font files when running in dev. Production builds were fine, but it kept getting the path wrong in dev. I managed to solve this by using a custom Valet driver. If you’re not sure what I mean, Valet uses drivers to help serve applications built with different frameworks. And there is a default Laravel driver that we can extend. Create a new PHP file at your project root called
LocalValetDriver.php and add the following:
That should do it. Valet will now use this driver instead of the default Laravel one. I found this to be a quick, simple solution to solving the FontSource issue and it should fix any other libraries with similar isues. All this is really doing is checking if the string
node_modules is in the requested URI and telling Valet that it’s not a static file and it can serve the url.
Since you’ll be running
build a bunch, you can add those as scripts to our
package.json file. Remove all the scripts that are there by default (these are for Laravel Mix which we’re not using anymore) and replace with two new ones:
Now when you want to start the dev server, you can just do:
Or if you’re ready to build for production:
Since everything now goes into a
build directory, it’s easy to add to your
.gitignore file. You shouldn’t be commiting that stuff to your repo.
Laravel and Inertia
If you’re a Laravel Mix person, then one thing that may seem confusing is that assets are now served from two different locations. In development, your assets are served from the
resources directory by Vites development server without being bundled. For production, your assets are served from
public/build. You may have guessed that this could be a bit of a challenge from a Laravel point of view. However, we have all the tools we need to make this work.
Inertia assumes that your base view file is
resources/views/app.blade.php. In this view you’ll need to check when it’s appropriate to load assets from the different locations. In production you can use the contents of the
build/manifest.json file to load the bundled assets, while Vite will take care of assets during development. You can make use of the
@production Blade directive to only target stuff that is meant for production. This means that we can change where assets are loaded from simply by changing the value of
That’s it. You made it. If you run
yarn dev and you’re able to get to your app through your normal Valet url, then you got it right. You should also be able to get to it without the dev server running if you first run
Vite has quickly become a big part of the tools I use daily. Let me know if you find all this useful.
One last stab at some self promotion
You don’t have to do this yourself every single time because I did it for you. I have a simple Laravel package called Vitamin which you can find here. It’s this simple:
composer require thepublicgood/vitaminphp ./artisan vitamin:init
It’s early days, so let me know if you find it useful.