Getting Started โ
Mono-repo is a way to build a large-scale, modular front-end on top of the Vue ecosystem. The goal is a maintainable, clean, complex project that takes minimum effort to grow.
Concept โ
Vue gives every project the same predictable shape:
src/pagesโ routingsrc/layoutsโ layouts
Inside a layout you usually find the same generic pieces: Sidebar, Navbar, Login, Logout, Middleware. Every normal website needs them.
Mono-repo makes this simple. Those generic pieces live once in the Host, and every Remote can use them without rebuilding.
A Remote only adds its own modules. It does not re-create Login, Logout, or any of the generic things โ the Host already handles all of that.
Because of this modular concept, the Host can have many Remotes, and all of them live inside the Host. Think about a house that have many rooms.
A Host can be a Vue app or a Nuxt app; a Remote is always a Vue app and works with either. See Setup for how each kind is wired.
Visual Example โ
One Host owns the shell; each finance module plugs in as its own Remote:
Host โ owns the shell of the app.
host/
โโโ src/
โโโ pages/ # login, logout, etc.
โโโ layouts/ # sidebar, navbar, etc.
โโโ ...etc/A Nuxt Host uses
app/instead ofsrc/(same folders inside). See Setup.
Remote โ just adds modules, for example a Budget module.
remote/
โโโ src/
โโโ pages/ # budget module
โโโ ...etc/Install โ
Both the Host and every Remote are standalone pnpm apps. They install the EJI libraries from a private git repo and pin every dependency through a pnpm workspace catalog โ a single pnpm-workspace.yaml that both apps share so their versions never drift. Your package.json then just points at the catalog with catalog:<group>.
Prerequisites โ
- pnpm (the catalog feature below needs a recent pnpm).
- A GitHub classic token โ the libraries live in the private
EJI-ICT/libsrepo, so the git URLs are token-authenticated. The snippets below use<GITHUB_CLASSIC_TOKEN>as a placeholder; replace it with your token.
pnpm mono:i is not ready yet
The mono-* templates already install their libraries through mono-cli, so their package.json says workspace:* where this page says catalog:internal. That is deliberate โ leave the templates as they are, and do not move your own project onto mono:i until it is announced as stable โ Mono CLI explains what it does and why it is not ready.
1. pnpm-workspace.yaml โ
This is the single source of truth for versions. The internal catalog pulls the three EJI libraries from git; the other groups pin the dependencies those libraries expect (their peers).
A Vue host & remote share the Vite-based catalog. A Nuxt host swaps the Vite plugins for Nuxt and its modules (a nuxt catalog group) โ pick the tab that matches your app.
yaml
shellEmulator: true
trustPolicy: no-downgrade
packages: []
catalogs:
# Internal EJI-ICT libraries (private git repo, token-authenticated)
internal:
mono-devextreme: git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-devextreme
mono-helper: git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-helper
mono-utils: git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-utils
# Build pipeline: Vite, bundler plugins and codegen tooling
build:
'@odata2ts/http-client-api': ^0.6.3
'@odata2ts/odata-query-objects': ^0.28.1
'@odata2ts/odata-service': ^0.23.1
'@odata2ts/odata2ts': ^0.40.1
'@vitejs/plugin-vue': ^6.0.7
unocss: ^66.7.0
unplugin-auto-import: ^21.0.0
unplugin-vue-components: ^32.1.0
vite: 8.0.16
vite-plugin-mkcert: ^2.1.0
vite-plugin-vue-devtools: ^8.1.2
vite-plugin-vue-layouts-next: ^2.1.0
# Dev tooling: type-checking, env helpers, upgrade/maintenance utilities
dev:
'@iconify/json': ^2.2.483
'@tsconfig/node22': ^22.0.5
'@vue/tsconfig': ^0.9.1
dotenv: ^17.4.2
dotenv-cli: ^11.0.0
taze: ^19.14.1
typescript: ~6.0.3
vue-tsc: ^3.3.3
# Frontend runtime: shipped to the browser bundle
frontend:
'@unhead/vue': 3.1.3
'@vueuse/core': 14.3.0
'@vueuse/integrations': 14.3.0
jwt-decode: ^4.0.0
notivue: ^2.4.5
pinia: 3.0.4
universal-cookie: ^8.1.2
vue: 3.5.24
vue-router: 5.0.4
yup: 1.7.1
# Type definitions
types:
'@types/node': ^25.9.2
onlyBuiltDependencies:
- esbuildyaml
shellEmulator: true
trustPolicy: no-downgrade
packages: []
catalogs:
# Internal EJI-ICT libraries (private git repo, token-authenticated)
internal:
mono-devextreme: git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-devextreme
mono-helper: git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-helper
mono-utils: git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-utils
# Nuxt framework + Nuxt modules (replaces the Vue Vite plugins)
nuxt:
nuxt: ^4.5.0
'@nuxt/kit': ^4.5.0 # build-time module API; pinned to Nuxt's version
'@pinia/nuxt': ^1.0.1
'@unocss/nuxt': ^66.7.5
'@vueuse/nuxt': 14.3.0
# Build pipeline: OData codegen (Nuxt bundles its own Vite)
build:
'@odata2ts/http-client-api': ^0.6.5
'@odata2ts/odata-query-objects': ^0.28.2
'@odata2ts/odata-service': ^0.23.2
'@odata2ts/odata2ts': ^0.40.2
unocss: ^66.7.5
# Dev tooling: type-checking and env helpers
dev:
'@iconify/json': ^2.2.501
dotenv: ^17.4.2
dotenv-cli: ^11.0.0
taze: ^19.14.1
# Stay on the classic (JS) TypeScript โ 7.0.x is the native Go port, which
# drops the compiler API `@vue/compiler-sfc` and `vue-tsc` still need.
typescript: ~6.0.3
vue-tsc: ^3.3.7
# Frontend runtime: shipped to the browser bundle
frontend:
'@sentry/nuxt': ^10.66.0
'@unhead/vue': 3.1.3
'@vueuse/core': 14.3.0
'@vueuse/integrations': 14.3.0
jwt-decode: ^4.0.0
notivue: ^2.4.5
pinia: 3.0.4
universal-cookie: ^8.1.2
vue: 3.5.24
vue-router: ^5.2.0
yup: 1.7.1
# Lit SSR: renders mono-helper's shadow components server-side.
# Both are EXACT pins, not ranges โ see the note under the table below.
lit:
nuxt-ssr-lit: 1.6.33
'@lit-labs/ssr': 3.2.2
# Type definitions
types:
'@types/node': ^26.1.1
onlyBuiltDependencies:
- esbuild
allowBuilds:
'@sentry/cli': false
core-js: false
esbuild: false
inferno: falseA Vue Host adds a few extras for error reporting (
@sentry/vue,@sentry/integrations,@sentry/vite-plugin); the Nuxt host tab already pins its equivalent (@sentry/nuxt). A Remote doesn't need them. The mono Nuxt modules ship inside the libraries (mono-utils/nuxt,mono-helper/nuxt), so there's nothing extra to install for them โ see Setup.
2. package.json โ
Reference the catalog with catalog:<group> โ pnpm resolves the version from pnpm-workspace.yaml, so you never repeat version numbers per app. The Vue tab covers both a Vue host and any Remote; the Nuxt host tab swaps the Vite plugins for nuxt + its modules.
json
{
"dependencies": {
"mono-devextreme": "catalog:internal",
"@unhead/vue": "catalog:frontend",
"@vueuse/core": "catalog:frontend",
"@vueuse/integrations": "catalog:frontend",
"dotenv": "catalog:dev",
"jwt-decode": "catalog:frontend",
"notivue": "catalog:frontend",
"pinia": "catalog:frontend",
"universal-cookie": "catalog:frontend",
"vue": "catalog:frontend",
"vue-router": "catalog:frontend",
"yup": "catalog:frontend"
},
"devDependencies": {
"mono-helper": "catalog:internal",
"mono-utils": "catalog:internal",
"@iconify/json": "catalog:dev",
"@odata2ts/http-client-api": "catalog:build",
"@odata2ts/odata-query-objects": "catalog:build",
"@odata2ts/odata-service": "catalog:build",
"@odata2ts/odata2ts": "catalog:build",
"@tsconfig/node22": "catalog:dev",
"@types/node": "catalog:types",
"@vitejs/plugin-vue": "catalog:build",
"@vue/tsconfig": "catalog:dev",
"dotenv-cli": "catalog:dev",
"taze": "catalog:dev",
"typescript": "catalog:dev",
"unocss": "catalog:build",
"unplugin-auto-import": "catalog:build",
"unplugin-vue-components": "catalog:build",
"vite": "catalog:build",
"vite-plugin-mkcert": "catalog:build",
"vite-plugin-vue-devtools": "catalog:build",
"vite-plugin-vue-layouts-next": "catalog:build",
"vue-tsc": "catalog:dev"
}
}json
{
"dependencies": {
"mono-devextreme": "catalog:internal",
"@sentry/nuxt": "catalog:frontend",
"@unhead/vue": "catalog:frontend",
"@vueuse/core": "catalog:frontend",
"@vueuse/integrations": "catalog:frontend",
"dotenv": "catalog:dev",
"jwt-decode": "catalog:frontend",
"notivue": "catalog:frontend",
"nuxt": "catalog:nuxt",
"pinia": "catalog:frontend",
"universal-cookie": "catalog:frontend",
"vue": "catalog:frontend",
"vue-router": "catalog:frontend",
"yup": "catalog:frontend"
},
"devDependencies": {
"mono-helper": "catalog:internal",
"mono-utils": "catalog:internal",
"@iconify/json": "catalog:dev",
"@lit-labs/ssr": "catalog:lit",
"@odata2ts/http-client-api": "catalog:build",
"@odata2ts/odata-query-objects": "catalog:build",
"@odata2ts/odata-service": "catalog:build",
"@odata2ts/odata2ts": "catalog:build",
"@pinia/nuxt": "catalog:nuxt",
"@types/node": "catalog:types",
"@unocss/nuxt": "catalog:nuxt",
"@vueuse/nuxt": "catalog:nuxt",
"dotenv-cli": "catalog:dev",
"nuxt-ssr-lit": "catalog:lit",
"taze": "catalog:dev",
"typescript": "catalog:dev",
"unocss": "catalog:build",
"vue-tsc": "catalog:dev"
}
}The Nuxt host drops the Vite-only plugins (@vitejs/plugin-vue, unplugin-*, vite-plugin-*) โ its mono-utils/nuxt + mono-helper/nuxt modules replace them โ and adds nuxt plus @pinia/nuxt / @unocss/nuxt / @vueuse/nuxt. Its lit catalog carries the pair that renders mono-helper's shadow components server-side โ see Setup.
Keep the
litpins exact.nuxt-ssr-lit@1.6.33needs@lit-labs/ssrv3: v4'srenderShadow()returns a thunk and drops the<!--lit-part-->markers, so every shadow component fails hydration with "no root part". A caret that picked up anuxt-ssr-litexpecting v4 would break SSR silently, which is why both sit in one catalog and move together.
3. Why these deps โ peer dependencies โ
The EJI libraries don't bundle their runtime dependencies; they declare them as peerDependencies. That means your app installs them once โ so there's a single copy of vue, vue-router, etc., and you control the versions (via the catalog above). The list above already covers them; here's the full picture so you know what each library expects:
| Library | Declares as peers (you must provide) |
|---|---|
mono-helper | (none) โ it bundles its own lit + flatpickr, so just install it. |
mono-utils | vue, vue-router, jwt-decode, @unhead/vue, dotenv, dotenv-cli, mono-devextreme, @odata2ts/http-client-fetch, notivue, yup |
mono-utils is self-contained โ import everything from mono-utils/runtime (useMonoUtility, MonoNotivue, useUtils, MonoOdataMapTypes, and every validation / OData type) or mono-utils/fetching. notivue and yup are optional peers (only if you use notifications / validation โ see the Notivue and Yup addons); @odata2ts/http-client-fetch is only needed when you fetch OData. The OData codegen peers (@odata2ts/*, etc.) live in your app's dev deps for type generation โ see OData Types.
4. Install โ
Install everything declared in package.json:
sh
pnpm iFinally, sync the federated apps and generate the aliases:
sh
pnpm mono:sync # mono sync && mono prepareNext, head to Setup to wire your Host and Remote. For the UI components and theming, continue with Mono-UI โ Getting started and Theme.