Skip to content

Repository files navigation

Angular Monaco with Monaco YAML example

Angular monaco example is example app for integrating Monaco, Monaco-YAML with Angular application.

Steps to integrate monaco with monaco-editor.

  • Install dependencies

    npm i monaco-editor monaco-yaml
  • Install dev dependencies

    npm i -D @angular-builders/custom-webpack monaco-editor-webpack-plugin style-loader css-loader@^5.2
    IMPORTANT

    double check the monaco version matrix

  • Create a custom webpack config.

    const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
    import * as webpack from 'webpack';
    
    export default (config: webpack.Configuration) => {
      config?.plugins?.push(
        new MonacoWebpackPlugin({
          // a ton of languages are lazily loaded by default, but we dont use any of them
          languages: ['json', 'yaml'],
          // we can disable features that we end up not needing/using
          features: [
            'accessibilityHelp',
            'anchorSelect',
            'bracketMatching',
            // 'browser',
            'caretOperations',
            'clipboard',
            // 'codeAction',
            // 'codelens',
            // 'colorPicker',
            'comment',
            'contextmenu',
            'copyPaste',
            'cursorUndo',
            // 'dnd',
            // 'documentSymbols',
            // 'dropIntoEditor',
            'find',
            'folding',
            // 'fontZoom',
            'format',
            'gotoError',
            'gotoLine',
            // 'gotoSymbol',
            'hover',
            // 'iPadShowKeyboard',
            // 'inPlaceReplace',
            'indentation',
            // 'inlayHints',
            'inlineCompletions',
            // 'inspectTokens',
            'lineSelection',
            'linesOperations',
            // 'linkedEditing',
            // 'links',
            // 'multicursor',
            // 'parameterHints',
            // 'quickCommand',
            // 'quickHelp',
            // 'quickOutline',
            // 'readOnlyMessage',
            // 'referenceSearch',
            // 'rename',
            'smartSelect',
            // 'snippet',
            'stickyScroll',
            'suggest',
            // 'toggleHighContrast',
            'toggleTabFocusMode',
            'tokenization',
            'unicodeHighlighter',
            // 'unusualLineTerminators',
            // 'viewportSemanticTokens',
            'wordHighlighter',
            'wordOperations',
            'wordPartOperations'
          ],
          customLanguages: [
            {
              label: 'yaml',
              entry: 'monaco-yaml',
              worker: {
                id: 'monaco-yaml/yamlWorker',
                entry: 'monaco-yaml/yaml.worker'
              }
            }
          ]
        })
      );
      // Remove the existing css loader rule
      const cssRuleIdx = config?.module?.rules?.findIndex((rule: any) => rule.test?.toString().includes(':css'));
      if (cssRuleIdx !== -1) {
        config?.module?.rules?.splice(cssRuleIdx!, 1);
      }
      config?.module?.rules?.push(
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
        },
        // webpack 4 or lower
        //{
        //  test: /\.ttf$/,
        //  use: ['file-loader'],
        //}
    
        // webpack 5
        {
          test: /\.ttf$/,
          type: 'asset/resource'
        }
      );
      return config;
    };
  • Modify angular.json

    1. modify architect→build→builder to use custom-webpack builder

    2. add customWebpackConfig to architect→build→builder→options

    3. modify architect→build→builder→options→styles to include monaco editor css

    4. update architect→serve block to use `custom-webpack builder.

        "projects": {
          "angular-monaco-example": {
             ...
              "architect": {
                  "build": {
                      "builder": "@angular-builders/custom-webpack:browser",
                      "options": {
                          "customWebpackConfig": {
                              "path": "./webpack.config.ts"
                          },
                          "styles": [
                              "node_modules/monaco-editor/min/vs/editor/editor.main.css",
                              ...
                              "src/styles.scss"
                          },
                          ...
                      }
                      ...
                  }
              },
              "serve": {
                "builder": "@angular-builders/custom-webpack:dev-server",
                "configurations": {
                  "production": {
                    "browserTarget": "angular-monaco-example:build:production"
                  },
                  "development": {
                    "browserTarget": "angular-monaco-example:build:development"
                  }
                },
                "defaultConfiguration": "development"
              },
          ...
  • Create an editor component as follows.

    import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
    import { Uri, editor } from 'monaco-editor/esm/vs/editor/editor.api';
    
    import 'monaco-editor';
    import { setDiagnosticsOptions } from 'monaco-yaml';
    
    @Component({
      selector: 'k-editor',
      templateUrl: './editor.component.html',
      styleUrls: ['./editor.component.scss']
    })
    export class EditorComponent implements OnInit {
      @ViewChild('editorContainer', { static: true }) _editorContainer!: ElementRef;
      codeEditorInstance!: editor.IStandaloneCodeEditor;
    
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      constructor() {}
    
      ngOnInit() {
        const modelUri = Uri.parse('a://b/foo.yaml');
        setDiagnosticsOptions({
          enableSchemaRequest: true,
          hover: true,
          completion: true,
          validate: true,
          format: true,
          schemas: [
            {
              // Id of the first schema
              uri: './assets/schemas/metallb-schema.json',
              // Associate with our model
              fileMatch: [String(modelUri)]
            }
            //}
          ]
        });
    
        this.codeEditorInstance = editor.create(this._editorContainer.nativeElement, {
          theme: 'vs-dark',
          //  wordWrap: 'on',
          //  wrappingIndent: 'indent',
          minimap: { enabled: true },
          automaticLayout: true,
          language: 'yaml',
          model: editor.createModel('', 'yaml', modelUri)
        });
      }
    }

Development server

  • Run ng serve for a dev server. Navigate to http://localhost:4200/. The application will automatically reload if you change any of the source files.

Lazy Loading Monaco Editor

This project uses Angular’s @defer block with a MonacoService to lazily load Monaco Editor only when needed, reducing the initial bundle size from ~4MB to ~315KB.

Architecture

  • MonacoService (src/app/services/monaco.service.ts) - Singleton service that:

    • Lazily loads Monaco via dynamic import()

    • Conditionally loads monaco-yaml only when language is YAML

    • Handles codicon font injection once

  • EditorComponent (src/app/editor/editor.component.ts) - Configurable editor with signal inputs:

    • language - Editor language (default: 'yaml')

    • theme - Editor theme (default: 'vs-dark')

    • schema - JSON schema configuration for validation

    • enableValidation, enableHover, enableCompletion, enableMinimap - Feature toggles

    • value - Two-way binding for editor content

Defer Trigger Options

Use these triggers in your template to control when Monaco loads:

Trigger Behavior

on viewport

Loads when element scrolls into view (best for visible content)

on interaction

Loads on click/focus/keydown on placeholder (explicit user intent)

on hover

Loads when user hovers over placeholder

on idle

Loads when browser is idle (background preloading)

on timer(500ms)

Loads after specified delay

when condition

Loads when expression becomes true (programmatic control)

Example Usage

@defer (on viewport; prefetch on idle) {
  <k-editor
    [language]="'yaml'"
    [theme]="'vs-dark'"
    [schema]="{ uri: './assets/schemas/my-schema.json' }"
    [enableValidation]="true"
    [(value)]="editorContent"
  />
} @placeholder {
  <div>Click to load editor</div>
} @loading {
  <div>Loading editor...</div>
}

Prefetch Strategy

Combine triggers with prefetch to silently load Monaco in the background:

  • prefetch on idle - Preload during browser idle time

  • prefetch on viewport - Preload when placeholder becomes visible

  • prefetch on timer(2s) - Preload after 2 seconds

Example: @defer (on interaction; prefetch on idle) shows a placeholder, preloads Monaco during idle, but only renders when user clicks.

About

Angular Monaco with Monaco YAML example

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages