Mixing Laravel 8 + VueJS 3 with TS in Laravel Mix

Have you ever imagined making Laravel a full SPA website?

In this post we'll learn how Laravel can perfectly fit with VueJS in order to create a full stack framework.

First of all it'd be great to understand how Laravel works and how it can make use of VueJS as template/view in the MVC architecture.

Normally, we use Laravel as a back-end framework in order to create either a webapp or a REST API. However, in case we make use of Laravel Mix, an incredible tool to compile CSS, JS etc into readable files using webpack, we are able to make any kind of app.

At the end of the post, I'll leave you the link of my repo where there's the final template and everything has been included.
Feel free to download it and use it as a base template for your projects.

Let's start !

First of all we'll focuse on the unique blade template that Laravel offers us when we create a Laravel project.

Basically it's a HTML file that will be rendered when X URL is set on the browser.

Now imagine if we implement VueJS on here. Just create a DIV tag with an ID. "app" for instance. We'll see how our Vue components will be all rendered in this place.

Once everything is set, we can start to download the libraries like loaders or compilers to convert VueJS, TypeScript, SASS, TailwindCSS or any other tech into readable files for the browser.

Files to download:

  • VueJS : npm i vue@next vue-router vue-loader @vue/compiler-sfc
  • TypeScript : npm i typescript ts-loader
  • SASS : npm i sass sass-loader
  • TailwindCSS : npm i tailwindcss

Check the versions match, specially with VueJS (vue & @vue/compiler-sfc)

If you've reached this part, the most difficult part should be done !

Now that we downloaded all the libraries, let's continue with this tutorial.

Open the file which is called "webpack.mix.js". It's in the root folder. We'll specify here what we want to compile in our project.
I leave you my config (VueJS 3 & TailwindCSS).

mix
  .ts("resources/ts/app.ts", "public/js")
  .vue({ version: 3 })
  .postCss("resources/css/app.css", "public/css", [require("tailwindcss")]);

You'll notice that I used typescript. So, let's set it up !

Go to the folder called "resources/". You'll find some other folders and files inside, mainly CSS and JS.
For the CSS, just write the tailwind directives in order to make use of it. (TailwindCSS installation)

For the JS, firstly, rename "js/" to "ts/" and the formats of every file inside of it. We'll focuse on "app.js". It's the file where we'll create our Vue app.

import { createApp } from "vue";

import App from "./vue/App.vue";
import router from "./router";

createApp(App).use(router).mount("#app");

As you can see, we've included the router... But that's not installed! We're not using Vue CLI or similar so... let's install it!

Actually, we already did it before but we need to implement it so let's create a file named "router.ts" next to "app.ts" and paste this config. We use the Web History mode to take advantage of SEO, etc.

import {
  createRouter,
  createWebHistory,
  RouteRecordRaw,
} from "vue-router";

import Home from "./vue/views/Home.vue";

const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    name: "Home page",
    component: Home,
  }
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;

If you're familiar to VueJS you should be able to understand the file.

Next, as you can guess, it's time to create our components as we'd do with Vue CLI or variants!!! Let's create our first one called "App.vue" inside a new folder called "vue/" and another one called "Home.vue" inside "vue/views/".
The tree would be "resources/vue/App.vue" and "resources/vue/views/Home.vue".

"vue/" nests "components/" for partial blocks of code and "views/" for pages.

Create your own content inside the new components and let's continue with the next step!

You must know App.vue is required! It'll be where we'll nest our views using the vue router (router-link & router-view)

IMPORTANT
You've noticed the project doesn't recognise the vue files... isn't that strange? Well, not actually, just we didn't specify we'd be using this kind of files.

Let's create a file called "shims-vue.d.ts" next to app.ts where we'll add this content to mean that we'll be using vue files:

declare module "*.vue" {
    import type { DefineComponent } from "vue";
    const component: DefineComponent<{}, {}, any>;
    export default component;
}

At this moment, we're done! Everything seems working properly, so let's continue!

To wrap up everything we've done, we installed all the necessary NPM libraries and created the vue components with TypeScript, compiling everything thanks to webpack.

However, there're some things to deal with before finishing this.

First of all, we must complete the blade template with the compiled files (VueJS with TS & TailwindCSS)!

Just link the CSS & JS (the result of the compile process) with the Laravel Mix syntax. The result should be as below:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Laravel Mix</title>

        <!-- META TAGS -->
        <meta name="author" content="IvaanTorres">
        <meta name="description" content="The MDN Web Docs Learning Area aims to provide
        complete beginners to the Web with all they need to know to get
        started with developing web sites and applications.">

        <!-- CSS -->
        <link rel="icon" href="{{ mix('favicon.ico') }}" type="image/x-icon">
        <link href="{{ mix('css/app.css') }}" rel="stylesheet">
        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
    </head>
    <body>
        <div id="app"></div>
    </body>
    <script src="{{ mix('js/app.js') }}"></script>
</html>

Et voilà! At this moment we would think that everything should work. Apparently, but not actually... There's a main point we should deal with. It's in the Laravel router !

Have you tried to use the vue router? Just add a router-link in App.vue to link another component, for example About.vue and compile everything.

  • Compile the project just once.
npm run dev
  • Compile the project after any change. The web will automatically be reloaded.
npm run hot

Once you've compiled all your project, now go to Home view, click on the link and try to refresh the web being in About view. What is this?! What's happening?! There's a 404 error.

This is because we're using the web history in the vue router. Try to change the mode to the hash mode and you'll find out the error dissapears... but we don't want that! So let's fix it!

Go to the laravel router (web.php). The "error" is in the route to "/". Basically our blade template will be rendered just in "/". That's not OK!

I've been getting crazy when I was dealing with this error, just I didn't know it would be that easy!

All you have to do is change the route to this:

Route::get('/{vue_capture?}', function () {
  return view('welcome');
})->where('vue_capture', '[\/\w\.-]*');

Basically we're telling PHP that any route we're typing on the browser will automatically redirect to our unique blade template.

Now, if we save changes and we try to use the router, we'll see everything works as expected!

Well, if you have reached this part completing (or not) this sort of tutorial, feel free to drop any comment below asking questions, etc.

If you didn't understand something, ask me and I'll be honored to help you :)

You can reach me here:
GitHub - IvaanTorres

37