Add a Vue Project

The code for this example is available on GitHub:

Official Vue Plugin

This recipe does not use the official Vue plugin, so it doesn't use generators or automate updating framework dependencies. Use @nx/vue to use those features.

Supported Features

We'll be using an Nx plugin 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

Keep Nx Package Versions In Sync

Make sure to install the @nx/vite and @nx/js versions that matches the version of nx in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can fix Nx version mismatches with this recipe.

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 as-provided vs. derived 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 important 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.