Mastodon Skip to main content
techreads

Add Vue.js + Vite to an AdonisJS Application

AdonisJS is a Node.js backend framework. There's no frontend framework coming with it, everything is rendered on the server side.

This article will describe how to integrate Vue.js (and Vite) to an AdonisJS application.

You should first create a new project with a "web" structure and no Webpack Encore integration:

CUSTOMIZE PROJECT
❯ Select the project structure · web
❯ Enter the project name · hello-world
❯ Setup eslint? (y/N) · false
❯ Configure webpack encore for compiling frontend assets? (y/N) · false

Add Vue.js & Vite dependencies & Configure Vite #

# if using npm
npm install vue && npm install --dev vite @vitejs/plugin-vue

# if using yarn
yarn add vue && yarn add -D vite @vitejs/plugin-vue

# if using pnpm
pnpm install vue && pnpm install -D vite @vitejs/plugin-vue

Create Vite configuration at the root of your project (vite.config.ts):

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
	return {
		base: command === 'serve' ? '' : '/build/',
		publicDir: 'fake_dir_so_nothing_gets_copied',
		build: {
			manifest: true,
			outDir: 'public/build',
			rollupOptions: {
				input: 'resources/js/app.ts',
			},
		},
		plugins: [vue()],
	};
});

Reconfigure app startup scripts #

You'll need a parallel script runner like npm-run-all:

# if using npm
npm install --dev npm-run-all

# if using yarn
yarn add -D npm-run-all

# if using pnpm
pnpm install -D npm-run-all

Then reconfigure your npm dev script this way (package.json excerpt):

{
	"scripts": {
		"dev": "run-p ace:serve vite:serve",
		"ace:serve": "node ace serve --watch",
		"vite:serve": "vite --host --port 3000"
	}
}

At this point, the npm dev command starts both AdonisJS & Vite. But we lack the integration of both: how AdonisJS will instruct your browser that it should load assets generated by Vite?

Integrate Vite into AdonisJS #

We'll create a ViteProvider class that will generate the contents to be included into AdonisJS pages:

Paste this into providers/ViteProvider.ts:

import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import Env from '@ioc:Adonis/Core/Env';
import { readFileSync } from 'fs';

export default class ViteProvider {
	public static needsApplication = true;

	constructor(protected app: ApplicationContract) {}

	public async boot() {
		const View = this.app.container.resolveBinding('Adonis/Core/View');

		const served = () => {
			const port = Env.get('VITE_PORT', 3000);
			return `
      <script type="module" src="http://localhost:${port}/@vite/client"></script>
      <script type="module" src="http://localhost:${port}/resources/vue/app.ts" ></script>
    `;
		};

		const built = () => {
			const data = readFileSync('./public/build/manifest.json').toString();
			const manifest = JSON.parse(data);
			return `<script type="module" src="/build/${manifest['resources/vue/app.ts']['file']}"></script>`;
		};
		View.registerTag({
			tagName: 'vite',
			seekable: false,
			block: false,
			compile(_, buffer) {
				buffer.outputRaw(
					Env.get('NODE_ENV') === 'development' ? served() : built(),
				);
			},
		});
	}
}

Register the provider into .adonisrc.json file (excerpt):

{
	"providers": ["./providers/ViteProvider"]
}

Replace default view contents (resources/views/welcome.edge):

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link rel="icon" type="image/png" href="/favicon.ico" />

		@vite ``
		<title>AdonisJS + Vue.js + Vite</title>
	</head>

	<body>
		<div id="app"></div>
	</body>
</html>

Create basic Vue.js application #

Create Vue.js application main entry point in resources/vue/app.ts:

import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

Create main component in resources/vue/App.vue:



Adjust for production build #

We need to adjust build scripts as well (package.json excerpt):

{
	"scripts": {
		"build": "run-s vite:build ace:build",
		"ace:build": "node ace build --production",
		"vite:build": "vite build"
	}
}

Our application will be build through the npm run build command.

This will fail because of the Vue.js code being processed by the TypeScript compiler.

To circumvent this, we add the Vue.js app folder to the TypeScript exclude paths (tsconfig.json excerpt):

{
	"exclude": ["resources/vue/", "node_modules", "build"]
}

Final thoughts #

This article gives the steps for a basic Vue.js integration into AdonisJS.

The developer experience can be enhanced by integrating a few more libraries:

Let's discuss about this on Mastodon!