Here is the most basic script that imports the editor using ESM with webpack.
More self-contained samples are available in the samples folder.
This is the easiest method, and it allows for options to be passed into the plugin in order to select only a subset of editor features or editor languages. Read more about the Monaco Editor WebPack Plugin, which is a community authored plugin.
index.js
import * as monaco from 'monaco-editor';
monaco.editor.create(document.getElementById('container'), {
value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
language: 'javascript'
});
webpack.config.js
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.ttf$/,
use: ['file-loader']
}
]
},
plugins: [new MonacoWebpackPlugin()]
};
Full working samples are available at https://github.com/microsoft/monaco-editor/tree/main/samples/browser-esm-webpack or https://github.com/microsoft/monaco-editor/tree/main/samples/browser-esm-webpack-small
index.js
import * as monaco from 'monaco-editor';
// Since packaging is done by you, you need
// to instruct the editor how you named the
// bundles that contain the web workers.
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
return './json.worker.bundle.js';
}
if (label === 'css' || label === 'scss' || label === 'less') {
return './css.worker.bundle.js';
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return './html.worker.bundle.js';
}
if (label === 'typescript' || label === 'javascript') {
return './ts.worker.bundle.js';
}
return './editor.worker.bundle.js';
}
};
monaco.editor.create(document.getElementById('container'), {
value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
language: 'javascript'
});
webpack.config.js
:
const path = require('path');
module.exports = {
entry: {
app: './index.js',
// Package each language's worker and give these filenames in `getWorkerUrl`
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
'css.worker': 'monaco-editor/esm/vs/language/css/css.worker',
'html.worker': 'monaco-editor/esm/vs/language/html/html.worker',
'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker'
},
output: {
globalObject: 'self',
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.ttf$/,
use: ['file-loader']
}
]
}
};
A full working sample is available at https://github.com/microsoft/monaco-editor/tree/main/samples/browser-esm-parcel
When using parcel, we need to use the getWorkerUrl
function and build the workers seperately from our main source. To simplify things, we can write a tiny bash script to build the workers for us.
index.js
import * as monaco from 'monaco-editor';
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
return './json.worker.js';
}
if (label === 'css' || label === 'scss' || label === 'less') {
return './css.worker.js';
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return './html.worker.js';
}
if (label === 'typescript' || label === 'javascript') {
return './ts.worker.js';
}
return './editor.worker.js';
}
};
monaco.editor.create(document.getElementById('container'), {
value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
language: 'javascript'
});
build_workers.sh
ROOT=$PWD/node_modules/monaco-editor/esm/vs
OPTS="--no-source-maps --log-level 1" # Parcel options - See: https://parceljs.org/cli.html
parcel build $ROOT/language/json/json.worker.js $OPTS
parcel build $ROOT/language/css/css.worker.js $OPTS
parcel build $ROOT/language/html/html.worker.js $OPTS
parcel build $ROOT/language/typescript/ts.worker.js $OPTS
parcel build $ROOT/editor/editor.worker.js $OPTS
Then, simply run sh ./build_workers.sh && parcel index.html
. This builds the workers into the same directory as your main bundle (usually ./dist
). If you want to change the --out-dir
of the workers, you must change the paths in index.js
to reflect their new location.
note - the getWorkerUrl
paths are relative to the build directory of your src bundle
Adding monaco editor to Vite is simple since it has built-in support for web workers. You only need to implement the getWorker
function (NOT the getWorkerUrl
) to use Vite's output (Source):
import * as monaco from 'monaco-editor';
self.MonacoEnvironment = {
getWorker: function (workerId, label) {
const getWorkerModule = (moduleUrl, label) => {
return new Worker(self.MonacoEnvironment.getWorkerUrl(moduleUrl), {
name: label,
type: 'module'
});
};
switch (label) {
case 'json':
return getWorkerModule('/monaco-editor/esm/vs/language/json/json.worker?worker', label);
case 'css':
case 'scss':
case 'less':
return getWorkerModule('/monaco-editor/esm/vs/language/css/css.worker?worker', label);
case 'html':
case 'handlebars':
case 'razor':
return getWorkerModule('/monaco-editor/esm/vs/language/html/html.worker?worker', label);
case 'typescript':
case 'javascript':
return getWorkerModule('/monaco-editor/esm/vs/language/typescript/ts.worker?worker', label);
default:
return getWorkerModule('/monaco-editor/esm/vs/editor/editor.worker?worker', label);
}
}
};
monaco.editor.create(document.getElementById('container'), {
value: "function hello() {\n\talert('Hello world!');\n}",
language: 'javascript'
});
Monaco Editor supports localization in multiple languages. When using the ESM version, you need to load the appropriate language files and configure the global NLS (National Language Support) variables.
The monaco-editor package includes translations for the following languages:
de
)es
)fr
)it
)ja
)ko
)zh-cn
)zh-tw
)To enable localization in your ESM integration, follow these steps:
Import the language file for your desired language before importing the editor:
// Import the German localization
import 'monaco-editor/esm/nls.messages.de.js';
// Import monaco editor after the language file
import * as monaco from 'monaco-editor';
// Your existing MonacoEnvironment setup
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
// ... your worker configuration
}
};
// Create editor - it will now use German localization
monaco.editor.create(document.getElementById('container'), {
value: 'function x() {\n\tconsole.log("Hello world!");\n}',
language: 'javascript'
});
For other languages, replace the import with the appropriate file:
// Spanish
import 'monaco-editor/esm/nls.messages.es.js';
// French
import 'monaco-editor/esm/nls.messages.fr.js';
// Italian
import 'monaco-editor/esm/nls.messages.it.js';
// Japanese
import 'monaco-editor/esm/nls.messages.ja.js';
// Korean
import 'monaco-editor/esm/nls.messages.ko.js';
// Chinese Simplified
import 'monaco-editor/esm/nls.messages.zh-cn.js';
// Chinese Traditional
import 'monaco-editor/esm/nls.messages.zh-tw.js';
The language files set up global variables that the editor uses for localization:
globalThis._VSCODE_NLS_MESSAGES
- Contains the translated stringsglobalThis._VSCODE_NLS_LANGUAGE
- Contains the language codeWhen you import a language file, it automatically sets these global variables, and the editor will use the translated strings for its UI elements like context menus, command palette, error messages, etc.
You can also load languages dynamically based on user preferences:
async function loadLanguage(language) {
if (language !== 'en') {
// Dynamically import the language file
await import(`monaco-editor/esm/nls.messages.${language}.js`);
}
// Import monaco after language is loaded
const monaco = await import('monaco-editor');
// Setup your editor
return monaco;
}
// Usage
loadLanguage('de').then((monaco) => {
monaco.editor.create(document.getElementById('container'), {
value: 'function x() {\n\tconsole.log("Hello world!");\n}',
language: 'javascript'
});
});
Here's a complete webpack example with German localization:
index.js
:
// Import German localization first
import 'monaco-editor/esm/nls.messages.de.js';
import * as monaco from 'monaco-editor';
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
return './json.worker.bundle.js';
}
if (label === 'css' || label === 'scss' || label === 'less') {
return './css.worker.bundle.js';
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return './html.worker.bundle.js';
}
if (label === 'typescript' || label === 'javascript') {
return './ts.worker.bundle.js';
}
return './editor.worker.bundle.js';
}
};
// Create editor with German UI
monaco.editor.create(document.getElementById('container'), {
value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
language: 'javascript'
});
The webpack configuration remains the same as shown in the plain webpack section.