Monaco Editor IntelliSense Configuration for Angular Projects
Learn how to configure Monaco Editor to provide IntelliSense for node_modules in Angular projects after worker setup changes in version 0.55.1. Complete guide with code examples.
How to configure Monaco Editor to provide IntelliSense for node_modules in an Angular project after worker setup changes in version 0.55.1?
I’m working on an Angular project and need to configure Monaco Editor to provide IntelliSense for node_modules. In monaco-editor@0.53.0, I could use the following approach:
monaco.languages.typescript.typescriptDefaults.addExtraLib(
rxjsIndex,
'file:///node_modules/rxjs/index.d.ts'
)
However, this approach no longer works in monaco-editor@0.55.1 due to the introduction of the worker setup. How can I achieve the same functionality in the newer version?
Here’s my current Monaco setup:
main.ts:
import { useInMemoryScrolling } from './shared/use-in-memory-scrolling.helper';
self.MonacoEnvironment = {
getWorkerUrl: (_moduleId: string, label: string) => {
const base = '/assets/monaco/';
switch (label) {
case 'json':
return base + 'language/json/json.worker.js';
case 'css':
return base + 'language/css/css.worker.js';
case 'html':
return base + 'language/html/html.worker.js';
case 'typescript':
case 'javascript':
return base + 'language/typescript/ts.worker.js';
default:
return base + 'editor/editor.worker.js';
}
}
};
app.ts:
@Component({
selector: 'app-root',
template: `<div #editor class="editor"></div>`,
styles: [`.editor{width:100%;height:100vh}`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit {
@ViewChild('editor', { static: true }) editorEl!: ElementRef<HTMLDivElement>
async ngOnInit(): Promise<void> {
monaco.languages.register({ id: 'typescript' });
const tm = monaco.editor.createModel(`class Test {test: string}\nconst x: Test = {}`, 'typescript',
monaco.Uri.parse('file:///main.ts'));
const editor = monaco.editor.create(this.editorEl.nativeElement, {
model: tm,
language: 'typescript',
theme: 'vs-dark',
});
}
}
What’s the proper way to configure Monaco Editor to provide IntelliSense for node_modules in an Angular project with the newer worker-based architecture?
Configuring Monaco Editor to provide IntelliSense for node_modules in an Angular project after worker setup changes in version 0.55.1 requires a different approach from the addExtraLib method. The key is to properly set up the TypeScript language service configuration with appropriate model URIs and ensure proper worker setup for the TypeScript language service. This involves configuring the TypeScript language service options, setting up proper file paths for node_modules, and ensuring the Monaco Editor can resolve imports from installed packages. The worker architecture changes in version 0.55.1 moved heavy computations to web workers, which affects how extra libraries and IntelliSense are configured.
Contents
- Understanding Monaco Editor Architecture Changes
- Configuring Monaco Editor for Angular Projects
- Setting Up IntelliSense for node_modules in Monaco Editor
- Worker Configuration for Monaco Editor 0.55.1+
- Troubleshooting Monaco Editor IntelliSense Issues
- Best Practices for Monaco Editor in Angular Applications
- Sources
- Conclusion
Understanding Monaco Editor Architecture Changes
The Monaco Editor underwent significant architectural changes in version 0.55.1, particularly in how it handles language services and worker setup. In the previous versions, you could directly add extra libraries using methods like addExtraLib, but the new architecture leverages web workers more extensively for computing heavy tasks outside of the UI thread.
According to the official Monaco Editor documentation, models are at the heart of the editor, representing files with content, language determination, and edit history tracking. Each model is identified by a URI, and proper URI selection is crucial for features like TypeScript import resolution.
The key change is that language services now create dedicated web workers to compute heavy features outside of the UI thread. This means that when working with TypeScript IntelliSense, including for node_modules, you need to ensure proper model URIs are used to enable TypeScript to resolve imports correctly.
Configuring Monaco Editor for Angular Projects
When setting up Monaco Editor in an Angular project, there are several critical configuration steps you need to follow to ensure proper IntelliSense functionality. The approach you’ve taken in your main.ts file is a good start, but there are additional considerations for Angular-specific integration.
First, ensure you’re loading the Monaco Editor assets properly in your Angular project. Typically, you would include the Monaco Editor files in your angular.json configuration:
"assets": [
"src/assets",
{
"glob": "**/*",
"input": "node_modules/monaco-editor/min/vs",
"output": "/assets/monaco/"
}
]
Next, modify your worker setup to include TypeScript language service configuration. Here’s an enhanced version of your main.ts:
import { useInMemoryScrolling } from './shared/use-in-memory-scrolling.helper';
self.MonacoEnvironment = {
getWorkerUrl: (_moduleId: string, label: string) => {
const base = '/assets/monaco/';
switch (label) {
case 'json':
return base + 'language/json/json.worker.js';
case 'css':
return base + 'language/css/css.worker.js';
case 'html':
return base + 'language/html/html.worker.js';
case 'typescript':
case 'javascript':
return base + 'language/typescript/ts.worker.js';
default:
return base + 'editor/editor.worker.js';
}
}
};
// Initialize Monaco Editor with TypeScript language service configuration
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false
});
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2017,
module: monaco.languages.typescript.ModuleKind.ESNext,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: ['node_modules/@types']
});
The key additions here are:
- Setting up TypeScript compiler options with proper module resolution
- Enabling semantic and syntax validation
- Configuring type roots to include node_modules/@types
Setting Up IntelliSense for node_modules in Monaco Editor
To properly configure IntelliSense for node_modules in the newer Monaco Editor versions, you need to take a different approach from the addExtraLib method. Instead, you should focus on configuring the TypeScript language service to resolve imports from installed packages.
Here’s how to modify your app.ts to enable proper IntelliSense for node_modules:
import { Component, OnInit, ElementRef, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import * as monaco from 'monaco-editor';
@Component({
selector: 'app-root',
template: `<div #editor class="editor"></div>`,
styles: [`.editor{width:100%;height:100vh}`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit {
@ViewChild('editor', { static: true }) editorEl!: ElementRef<HTMLDivElement>
async ngOnInit(): Promise<void> {
// Register TypeScript language
monaco.languages.register({ id: 'typescript' });
// Configure TypeScript language service
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false,
noSuggestionDiagnostics: false
});
// Set compiler options for proper module resolution
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2017,
module: monaco.languages.typescript.ModuleKind.ESNext,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: ['node_modules/@types'],
allowJs: true,
checkJs: true,
experimentalDecorators: true,
emitDecoratorMetadata: true
});
// Create a model with proper URI for TypeScript IntelliSense
const tm = monaco.editor.createModel(
`import { Observable } from 'rxjs';\n\n` +
`class Test {\n` +
` test: string;\n` +
`}\n\n` +
`const x: Test = {};\n` +
`const obs: Observable<number> = new Observable();`,
'typescript',
monaco.Uri.parse('file:///src/main.ts')
);
const editor = monaco.editor.create(this.editorEl.nativeElement, {
model: tm,
language: 'typescript',
theme: 'vs-dark',
automaticLayout: true,
fontSize: 14,
minimap: { enabled: false },
scrollBeyondLastLine: false,
wordWrap: 'on'
});
// Enable TypeScript IntelliSense for node_modules
this.enableNodeModulesIntelliSense(editor);
}
private enableNodeModulesIntelliSense(editor: monaco.editor.IStandaloneCodeEditor): void {
// Get the TypeScript language service
const tsWorker = (editor as any)._modelData._languageService._worker;
if (tsWorker) {
tsWorker.then((worker: any) => {
// Configure worker to resolve node_modules
worker.getProxy().then((proxy: any) => {
// Set up additional file system providers if needed
monaco.languages.typescript.javascriptDefaults.addExtraLib(
`/// <reference path="node_modules/@types/node/index.d.ts" />`,
'file:///node_modules/@types/node/index.d.ts'
);
// Add reference to common node_modules types
const commonLibs = [
'rxjs',
'@angular/core',
'@angular/common',
'@angular/platform-browser',
'lodash',
'moment'
];
commonLibs.forEach(lib => {
monaco.languages.typescript.javascriptDefaults.addExtraLib(
`/// <reference path="node_modules/${lib}/index.d.ts" />`,
`file:///node_modules/${lib}/index.d.ts`
);
});
});
});
}
}
}
The critical improvements in this approach include:
-
Proper URI Configuration: Using
monaco.Uri.parse('file:///src/main.ts')helps TypeScript understand the context of your file. -
Compiler Options: Setting appropriate compiler options including
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJsenables proper import resolution. -
TypeScript Language Service Configuration: Enabling semantic validation and setting up type roots to include node_modules/@types.
-
Worker-based IntelliSense: Accessing the TypeScript worker through the editor model to configure IntelliSense for node_modules.
Worker Configuration for Monaco Editor 0.55.1+
The worker architecture changes in Monaco Editor 0.55.1+ require careful configuration to ensure proper IntelliSense functionality. As noted in the GitHub issues, the editor must be loaded with a web server on http:// or https:// schemes, as HTML5 does not allow pages loaded on file:// to create web workers.
Here’s an enhanced worker configuration for Angular:
// monaco-config.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MonacoConfigService {
configureMonacoEnvironment(): void {
// Set up Monaco worker URLs
self.MonacoEnvironment = {
getWorkerUrl: (_moduleId: string, label: string) => {
const base = '/assets/monaco/';
switch (label) {
case 'json':
return `${base}language/json/json.worker.js`;
case 'css':
return `${base}language/css/css.worker.js`;
case 'html':
return `${base}language/html/html.worker.js`;
case 'typescript':
case 'javascript':
return `${base}language/typescript/ts.worker.js`;
default:
return `${base}editor/editor.worker.js`;
}
}
};
// Configure TypeScript language service
this.configureTypeScriptLanguageService();
}
private configureTypeScriptLanguageService(): void {
// Enable TypeScript IntelliSense
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false,
noSuggestionDiagnostics: false
});
// Set up compiler options for node_modules resolution
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2017,
module: monaco.languages.typescript.ModuleKind.ESNext,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: [
'node_modules/@types',
'./node_modules/@types'
],
allowJs: true,
checkJs: true,
experimentalDecorators: true,
emitDecoratorMetadata: true,
skipLibCheck: false,
strict: true,
noImplicitAny: false,
strictNullChecks: false,
strictFunctionTypes: false,
strictBindCallApply: false,
strictPropertyInitialization: false,
noImplicitThis: false,
alwaysStrict: false,
noUnusedLocals: false,
noUnusedParameters: false,
noImplicitReturns: false,
noFallthroughCasesInSwitch: false
});
// Add node_modules type definitions
this.addNodeModulesTypeDefinitions();
}
private addNodeModulesTypeDefinitions(): void {
// Add common type definitions from node_modules
const typeDefinitions = [
{
path: 'node_modules/@types/node/index.d.ts',
content: '/// <reference types="node" />\n'
},
{
path: 'node_modules/@types/lodash/index.d.ts',
content: '/// <reference types="lodash" />\n'
},
{
path: 'node_modules/@types/rxjs/index.d.ts',
content: '/// <reference types="rxjs" />\n'
}
];
typeDefinitions.forEach(def => {
try {
fetch(def.path)
.then(response => response.text())
.then(content => {
monaco.languages.typescript.javascriptDefaults.addExtraLib(
content,
`file:///${def.path}`
);
})
.catch(error => {
console.warn(`Failed to load type definition: ${def.path}`, error);
});
} catch (error) {
console.warn(`Error loading type definition: ${def.path}`, error);
}
});
}
}
This service-based approach provides better organization and reusability of Monaco Editor configuration in your Angular application.
Troubleshooting Monaco Editor IntelliSense Issues
Even with proper configuration, you might encounter issues with Monaco Editor IntelliSense in Angular projects. Here are some common problems and their solutions:
1. Worker Loading Issues
If workers fail to load, ensure your angular.json includes the Monaco Editor assets correctly:
"assets": [
"src/assets",
{
"glob": "**/*",
"input": "node_modules/monaco-editor/min/vs",
"output": "/assets/monaco/"
}
],
"styles": [
"node_modules/monaco-editor/min/vs/editor/editor.main.css"
]
2. URI Resolution Problems
If Monaco Editor can’t resolve node_modules, verify your compiler options:
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2017,
module: monaco.languages.typescript.ModuleKind.ESNext,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: [
'node_modules/@types',
'./node_modules/@types',
'/node_modules/@types'
],
baseUrl: './',
paths: {
'*': ['node_modules/*']
}
});
3. Type Definition Loading Issues
If type definitions aren’t loading properly, try adding them programmatically:
async function loadTypeDefinitions(): Promise<void> {
const typeLibs = [
'node_modules/@types/node/index.d.ts',
'node_modules/@types/lodash/index.d.ts',
'node_modules/@types/rxjs/index.d.ts'
];
for (const libPath of typeLibs) {
try {
const response = await fetch(libPath);
if (response.ok) {
const content = await response.text();
monaco.languages.typescript.javascriptDefaults.addExtraLib(
content,
`file:///${libPath}`
);
}
} catch (error) {
console.warn(`Failed to load type definition: ${libPath}`, error);
}
}
}
4. Performance Issues with Large Projects
For large Angular projects, consider optimizing Monaco Editor performance:
// Configure performance settings
monaco.editor.create(this.editorEl.nativeElement, {
model: tm,
language: 'typescript',
theme: 'vs-dark',
automaticLayout: true,
fontSize: 14,
minimap: {
enabled: false,
showSlider: 'mouseover'
},
scrollBeyondLastLine: false,
wordWrap: 'on',
lineNumbers: 'on',
renderWhitespace: 'selection',
renderLineHighlight: 'all',
occurrencesHighlight: 'singleFile',
cursorBlinking: 'blink',
mouseWheelZoom: true,
quickSuggestions: {
other: true,
comments: true,
strings: true
},
suggestOnTriggerCharacters: true,
acceptSuggestionOnEnter: 'on',
tabCompletion: 'on',
parameterHints: {
enabled: true
},
wordBasedSuggestions: 'allDocuments',
suggestSelection: 'first'
});
Best Practices for Monaco Editor in Angular Applications
When integrating Monaco Editor into Angular applications, following best practices ensures optimal performance and maintainability:
1. Service-Based Configuration
Create a dedicated service for Monaco Editor configuration:
// monaco.service.ts
import { Injectable } from '@angular/core';
import { MonacoEditorLoaderService } from '@materia/monaco-editor';
import * as monaco from 'monaco-editor';
@Injectable({
providedIn: 'root'
})
export class MonacoService {
constructor(private monacoLoader: MonacoEditorLoaderService) {}
async initialize(): Promise<void> {
await this.monacoLoader.loadMonaco();
this.configureMonacoEnvironment();
this.configureTypeScriptLanguageService();
}
private configureMonacoEnvironment(): void {
// Worker configuration
self.MonacoEnvironment = {
getWorkerUrl: (_moduleId: string, label: string) => {
return `/assets/monaco/${this.getWorkerFileName(label)}`;
}
};
}
private getWorkerFileName(label: string): string {
switch (label) {
case 'json':
return 'language/json/json.worker.js';
case 'css':
return 'language/css/css.worker.js';
case 'html':
return 'language/html/html.worker.js';
case 'typescript':
case 'javascript':
return 'language/typescript/ts.worker.js';
default:
return 'editor/editor.worker.js';
}
}
private configureTypeScriptLanguageService(): void {
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false
});
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2017,
module: monaco.languages.typescript.ModuleKind.ESNext,
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: ['node_modules/@types'],
allowJs: true,
checkJs: true
});
}
}
2. Lazy Loading Monaco Editor
Implement lazy loading to improve application startup performance:
// monaco-editor.component.ts
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { MonacoService } from './monaco.service';
import { Subscription } from 'rxjs';
import * as monaco from 'monaco-editor';
@Component({
selector: 'app-monaco-editor',
template: `<div #editor class="monaco-editor"></div>`
})
export class MonacoEditorComponent implements OnInit, OnDestroy {
@ViewChild('editor', { static: true }) editorEl!: ElementRef<HTMLDivElement>;
private monacoSubscription: Subscription;
private editor: monaco.editor.IStandaloneCodeEditor;
constructor(private monacoService: MonacoService) {}
ngOnInit(): void {
this.monacoSubscription = this.monacoService.initialize().subscribe(() => {
this.createEditor();
});
}
private createEditor(): void {
this.editor = monaco.editor.create(this.editorEl.nativeElement, {
value: '// Your code here',
language: 'typescript',
theme: 'vs-dark',
automaticLayout: true
});
}
ngOnDestroy(): void {
this.monacoSubscription?.unsubscribe();
this.editor?.dispose();
}
}
3. Proper Error Handling
Implement robust error handling for Monaco Editor operations:
// monaco-error-handler.ts
export class MonacoErrorHandler {
static handleInitializationError(error: Error): void {
console.error('Monaco Editor initialization failed:', error);
// Implement fallback behavior or user notification
}
static handleWorkerError(error: Error): void {
console.error('Monaco Editor worker error:', error);
// Implement error recovery or graceful degradation
}
static handleTypeDefinitionError(error: Error, libPath: string): void {
console.warn(`Failed to load type definition: ${libPath}`, error);
// Implement alternative type resolution or graceful degradation
}
}
4. Performance Optimization
For large applications, implement performance optimizations:
// monaco-performance.ts
export class MonacoPerformanceOptimizer {
static configurePerformanceSettings(editor: monaco.editor.IStandaloneCodeEditor): void {
editor.updateOptions({
quickSuggestions: {
other: true,
comments: true,
strings: true
},
suggestOnTriggerCharacters: true,
acceptSuggestionOnEnter: 'on',
tabCompletion: 'on',
parameterHints: {
enabled: true
},
});
monaco.editor.setTheme('vs-dark');
}
static optimizeWorkerUsage(): void {
// Configure worker pool size based on available resources
if ((window as any).MonacoEnvironment) {
// Custom worker configuration
}
}
}
Sources
- Microsoft Monaco Editor Documentation — Core architecture and worker configuration: https://github.com/microsoft/monaco-editor
- GitHub Issues for Monaco Editor — Discussion on worker setup and IntelliSense configuration: https://github.com/microsoft/monaco-editor/issues
- Monaco Editor Samples — Examples and integration patterns for various frameworks: https://github.com/microsoft/monaco-editor-samples
Conclusion
Configuring Monaco Editor to provide IntelliSense for node_modules in an Angular project after worker setup changes in version 0.55.1 requires a comprehensive approach that addresses the new architecture. The key is to properly configure the TypeScript language service with appropriate compiler options, set up proper model URIs, and ensure the worker environment is correctly initialized.
By following the service-based configuration approach outlined in this guide, you can successfully enable IntelliSense for node_modules while maintaining optimal performance in your Angular application. Remember to handle errors gracefully, implement proper lazy loading, and optimize performance settings for your specific use case.
The transition from the addExtraLib approach to the worker-based architecture requires understanding how Monaco Editor now handles language services through web workers, but with proper configuration, you can achieve robust IntelliSense support for all your node_modules dependencies.

The Monaco Editor is the fully featured code editor from VS Code that uses web workers to compute heavy tasks outside of the UI thread. Models are at the heart of Monaco editor, representing files with content, language determination, and edit history tracking. Each model is identified by a URI, and proper URI selection is important for features like TypeScript import resolution. The ESM version of the editor is compatible with webpack and other modern bundlers, making it ideal for Angular projects.

Language services in Monaco Editor create web workers to compute heavy features outside of the UI thread. The editor must be loaded with a web server on http:// or https:// schemes as HTML5 does not allow pages loaded on file:// to create web workers. For proper TypeScript IntelliSense including node_modules, developers need to ensure proper model URIs are used to enable TypeScript to resolve imports correctly.

The monaco-editor-samples repository has been merged into the main monaco-editor repository. This repository contains examples and samples for implementing Monaco Editor in various frameworks and scenarios. For Angular projects, developers can reference these samples to understand proper integration patterns, especially for handling worker setup and IntelliSense configuration.