Add a Vue Project

The code for this example is available on Github:

Supported Features

We'll be using an Nx plugin for Vue called @nx/vite. Although we are using @nx/vite, not all dependencies will be able to be automatically updated. So we'll have to update any framework dependencies as needed.

✅ Run Tasks ✅ Cache Task Results ✅ Share Your Cache ✅ Explore the Graph ✅ Distribute Task Execution ✅ Integrate with Editors ✅ Automate Updating Nx ✅ Enforce Module Boundaries ✅ Use Task Executors 🚫 Use Code Generators 🚫 Automate Updating Framework Dependencies

Setup workspace

Create a new Nx workspace

create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=true

Add @nx/vite, vue, and other dependencies to your workspace

npm install --save-dev @nx/vite @nx/js vue vue-tsc vitest vite-tsconfig-paths vite-plugin-dts vite @vitejs/plugin-vue @vitejs/plugin-vue-jsx

Nx 15 and lower use @nrwl/ instead of @nx/

Create the application

touch index.html

And add the following content:

1<!DOCTYPE html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <title>Acme</title> 6 7 <meta name="viewport" content="width=device-width, initial-scale=1" /> 8 <link rel="icon" type="image/x-icon" href="./favicon.ico" /> 9 </head> 10 <body> 11 <div id="app"></div> 12 <script type="module" src="./src/main.ts"></script> 13 </body> 14</html> 15

Navigate to src/index.ts and change it to src/main.ts and add the following content:

1import { createApp } from 'vue'; 2import App from './App.vue'; 3 4createApp(App).mount('#app'); 5

Create a new file src/App.vue with the following content:

1<script setup lang="ts"> 2import { ref } from 'vue'; 3 4const count = ref(0); 5 6function increment() { 7 count.value++; 8} 9</script> 10<template> 11 <div>count is {{ count }}</div> 12 <button @click="increment">Increment</button> 13</template> 14

Configure Nx to use build and serve your Vue application

Navigate to vite.config.ts and add the following content:

1// Add this to your imports 2import vue from '@vitejs/plugin-vue'; 3import vueJsx from '@vitejs/plugin-vue-jsx'; 4 5export default defineConfig({ 6 plugins: [ 7 //.... other plugins 8 vue(), 9 vueJsx(), 10 ], 11}); 12

Create a new file env.d.ts for example at the root of the project and add the following content:

1/// <reference types="vite/client" /> 2/* eslint-disable */ 3declare module '*.vue' { 4 import type { DefineComponent } from 'vue'; 5 // eslint-disable-next-line @typescript-eslint/no-explicit-any 6 const component: DefineComponent<object, object, any>; 7 export default component; 8} 9

We need this file to ensure that Vue types are available to the compiler.

Update your tsconfig.lib.json to be tsconfig.app.json an add the following content:

1{ 2 "extends": "./tsconfig.json", 3 "compilerOptions": { 4 "outDir": "dist/out-tsc", 5 "types": ["node", "vite/client"], 6 "jsxImportSource": "vue" 7 }, 8 "files": [], 9 "exclude": [ 10 "src/**/*.spec.ts", 11 "src/**/*.test.ts", 12 "src/**/*.spec.tsx", 13 "src/**/*.test.tsx", 14 "src/**/*.spec.js", 15 "src/**/*.test.js", 16 "src/**/*.spec.jsx", 17 "src/**/*.test.jsx" 18 ], 19 "include": [ 20 "src/**/*.ts", 21 "src/**/*.d.ts", 22 "src/**/*.tsx", 23 "**/*.vue", 24 "vite.config.ts", 25 "env.d.ts" 26 ] 27} 28

We include vite.config.ts and env.d.ts to ensure that the types are available to the compiler.

Navigate to project.json and update it with the following content:

1 "build": { 2 "executor": "@nx/vite:build", 3 "outputs": ["{options.outputPath}"], 4 "defaultConfiguration": "production", 5 "options": { 6 "outputPath": "dist/acme" 7 }, 8 "configurations": { 9 "development": { 10 "mode": "development" 11 }, 12 "production": { 13 "mode": "production" 14 } 15 } 16 }, 17 "serve": { 18 "executor": "@nx/vite:dev-server", 19 "defaultConfiguration": "development", 20 "options": { 21 "buildTarget": "acme:build" 22 }, 23 "configurations": { 24 "development": { 25 "buildTarget": "acme:build:development", 26 "hmr": true 27 }, 28 "production": { 29 "buildTarget": "acme:build:production", 30 "hmr": false 31 } 32 } 33 }, 34
Nx 15 and lower use @nrwl/ instead of @nx/

This allows us to use nx build and nx serve to build and serve our Vue application.

Test it out

Build

nx build acme

Serve

nx serve acme

Create a library

Instead of having our Counter directly defined in the app we can instead create a library that exports the Counter component.

Directory Flag Behavior Changes

The command below uses the as-provided directory flag behavior, which is the default in Nx 16.8.0. If you're on an earlier version of Nx or using the derived option, omit the --directory flag. See the workspace layout documentation for more details.

Create a new library nx generate @nx/js:library --name=Counter --directory=libs/counter --unitTestRunner=vitest --bundler=vite --importPath=@acme/counter

Create your Counter component at counter/src/lib/Counter.vue and copy the contents of your src/App.vue into it.

Update your libs/counter/src/lib/index.ts to export your Counter component.

1export { default as Counter } from './Counter.vue'; 2

The default is very import here as it allows us to import the component using import { Counter } from '@acme/counter' instead of import Counter from '@acme/counter'.

Update your root ./vite.config.ts to include the following:

1export default defineConfig({ 2 //... other config 3 resolve: { 4 alias: { 5 '@acme/counter': fileURLToPath( 6 new URL('./counter/src/index.ts', import.meta.url) 7 ), 8 }, 9 }, 10}); 11

This allows the runtime to resolve the @acme/counter import to the correct location.

Finally update your src/App.vue to use the Counter component.

1<script setup lang="ts"> 2import { Counter } from '@acme/counter'; 3</script> 4<template> 5 <Counter /> 6</template> 7

Test it out

Build

nx build acme

Serve

nx serve acme

More Documentation

A larger example including libraries, tests, and more is available at Nx Vue Example on Github.