# `@volar/kit`, `@volar/editor`, and `@volar/monaco` Deep Dive > [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Configuration & Projects](configuration-and-projects.md) • [Alternative Editors](editor-integration-alt.md) These companion packages encapsulate Volar’s runtime wiring so you can spin up language servers, custom editors, or browser-based playgrounds without rebuilding the infrastructure each time. This guide documents every API surface, lifecycle hook, and best practice for: - `@volar/kit` – batteries-included Node bootstrapper for LSP servers (watchers, configuration, project factories). - `@volar/editor` – minimal abstractions for embedding Volar in non-LSP editors (custom document/connection implementations). - `@volar/monaco` – drop-in helpers for wiring Volar’s language service into Monaco-based experiences (VS Code Web, StackBlitz, custom playgrounds). ## 1. `@volar/kit` ### Purpose `@volar/kit` handles the repetitive work of: - Scaffolding an LSP server with sensible defaults. - Loading `tsconfig` projects (optionally with project references). - Watching the file system for changes and triggering reloads/re-diagnostics. - Exposing configuration hooks and restart commands. ### Quickstart ```ts import { createLanguageServer } from '@volar/kit'; import * as ts from 'typescript'; import { createVueProject } from '@vue/language-core'; const server = createLanguageServer({ ts, loadProjects(rootFolders) { return rootFolders.map((folder) => createVueProject(ts, { rootUri: folder, tsconfig: `${folder.fsPath}/tsconfig.json`, }), ); }, }); server.start(); ``` ### Lifecycle Hooks | Hook | Signature | Use | | --- | --- | --- | | `onCreate` | `(server) => void` | Add custom commands, middleware, telemetry. | | `loadProjects` | `(folders: URI[]) => Project[]` | Return an array of project instances; each project exposes `getLanguageService` and TS integration hooks. | | `watchFilePatterns` | `string[]` | Additional globs for the internal FS watcher. | | `resolveConfig` | `(connection) => Promise` | Async configuration loader; automatically re-applied on `workspace/didChangeConfiguration`. | ### Configuration Options | Option | Description | | --- | --- | | `ts` | TypeScript module (required). | | `tsLocalized` | Map of localized diagnostic messages (optional). | | `projectFeatures.workspaceDiagnostics` | Enable workspace diagnostics handling (`true` by default when the client supports it). | | `projectFeatures.interFileDependencies` | When `true`, diagnostics consider cross-file dependencies; `false` means per-file only. | | `projectFeatures.strict` | Equivalent to `vue-tsc --strict`; surfaces additional diagnostics. | ### Best Practices 1. **Single source for TypeScript** – pass the same `ts` object everywhere (`@volar/kit`, plugin code, CLI) to avoid version mismatches. 2. **Debounce reloads** – if you implement custom watchers, wrap `project.reload()` calls with a debounce to prevent thrashing. 3. **Expose restart commands** – `server.commands.register('volar.restart', () => server.restart())` so users can force reload from the editor. ## 2. `@volar/editor` ### Purpose Designed for editors that don’t use LSP (custom IDEs, in-house editors). It provides building blocks for: - Managing documents (`TextDocument`, `SnapshotDocument`). - Routing requests to the language service without JSON-RPC. - Handling workspace edits, diagnostics, and commands manually. ### Key APIs - `createEditorConnection(languageService)` – returns an object with methods mirroring LSP requests (`doValidation`, `doComplete`, etc.) but callable directly. - `SnapshotDocument` – text document implementation that tracks incremental updates and script snapshots. - `TextDocuments` – event emitter (`onDidOpen`, `onDidChangeContent`, etc.) you can wire to your editor’s change notifications. ### Usage Pattern ```ts import { createEditorConnection } from '@volar/editor'; import { createLanguageService } from '@volar/language-service'; const language = createLanguage(...); const service = createLanguageService(language, plugins, env, project); const editor = createEditorConnection(service); editor.documents.onDidOpen(({ document }) => { const diagnostics = editor.languageFeatures.getDiagnostics(document.uri); applyDiagnostics(document.uri, diagnostics); }); editor.documents.onDidChangeContent(({ document }) => { const completions = editor.languageFeatures.getCompletionItems(document.uri, position); showCompletions(completions); }); ``` ### When to Use - Custom Electron apps that embed Monaco but want offline/deterministic routing. - In-house IDEs (e.g., game studios) that prefer direct API calls over LSP. ## 3. `@volar/monaco` ### Purpose Wraps Volar’s language service to integrate seamlessly with Monaco editor (used in VS Code Web, StackBlitz, CodeSandbox, Vue SFC Playground). ### Setup Steps 1. **Worker Registration** ```ts import * as monaco from 'monaco-editor'; import { setupLanguageServiceForMonaco } from '@volar/monaco'; setupLanguageServiceForMonaco(monaco, { ts: () => import('typescript'), service: () => import('your-volar-service-entry'), }); ``` - Provide async loaders for TypeScript and your language service bundle. - Bundlers like Vite or Webpack should treat the worker entry as `worker` output. 2. **Virtual File System** Implement a file host used by the language service: ```ts const files = new Map(); const fsHost = { readFile(uri: string) { return files.get(uri); }, writeFile(uri: string, content: string) { files.set(uri, content); }, getDirectoryEntries(uri: string) { // return child URIs }, }; ``` 3. **Synchronizing Monaco Models** Listen to Monaco model events and forward them to Volar: ```ts monaco.editor.onDidCreateModel((model) => { const uri = model.uri.toString(); fsHost.writeFile(uri, model.getValue()); model.onDidChangeContent(() => { fsHost.writeFile(uri, model.getValue()); languageService.syncFile(uri, model.getValue()); }); }); ``` ### Bundling Tips - Use dynamic imports to lazy-load the worker bundle. - Ensure TypeScript is available in the worker scope (via `ts = await loadTS()`). - Provide polyfills for `fetch`/`FileReader` in browsers that lack them. ### Features Enabled - IntelliSense for `.vue`/custom files. - Diagnostics inline in Monaco (markers API). - Formatting via Monaco’s `languages.registerDocumentFormattingEditProvider`. ## Combining the Pieces | Scenario | Recommended Package | | --- | --- | | Node-based LSP server | `@volar/kit` | | Custom desktop editor | `@volar/editor` | | Browser playground | `@volar/monaco` | | Hybrid (Node server + Monaco client) | `@volar/kit` on the backend + `@volar/monaco` on the frontend | ## Cross-Cutting Best Practices 1. **Shared Language Service Factory** – keep your `createLanguageService` logic in one module and import it in both LSP (+@volar/kit) and Monaco contexts to avoid divergence. 2. **File System Abstraction** – design a file host interface that works for Node (fs/promises) and browser (in-memory map or IndexedDB). 3. **Configuration Sync** – if you expose custom settings, ensure both LSP + Monaco flows consume the same configuration object (`applyConfiguration(config)`). 4. **Version Alignment** – lock `@volar/*` package versions across server and client to prevent subtle mismatches. 5. **Telemetry Parity** – forward the same telemetry/logging events from both LSP and Monaco contexts so you can compare editor behaviors. By understanding the responsibilities of `@volar/kit`, `@volar/editor`, and `@volar/monaco`, you can implement Volar-powered experiences everywhere—from desktop editors to in-browser playgrounds—without reimplementing the plumbing each time.