» npm install @monaco-editor/react
Videos
I’m currently working on integrating Monaco Editor into a React TypeScript project to support multiple languages (JavaScript, TypeScript, Python, Java) with IntelliSense using Language Server Protocol (LSP). I’ve been struggling to find clear and working resources or examples for this setup.
My Challenges:
Configuring Monaco Editor to work with multiple languages.
Setting up LSP to enable IntelliSense (autocomplete, error highlighting).
What I’m Looking For:
Guidance on how to set up Monaco Editor with LSP in React TS.
Any working examples, code snippets, or reliable documentation links.
I’ve tried various tutorials, but none seem to work fully or fit my requirements. I would be really grateful for any help, advice, or pointers you can provide!
lsp #monaco #language-server
I am working on a monaco editor with my own language definition (mylang, it's a js file). Frontend is working with HTML. note: the editor has to be compatible with react (maybe its not even necessary to implement the editor as a react component?)
turning the monaco editor into a react component works, but as soon as i link the language definition file to the editor, it stops working and i get the require is not defined error and if i do it without the require function it doesnt recognize the editor anymore.
<!-- load language, somehow its not working with our language definition (mylang.js). there is a problem with the require command in mylang -->
<!-- in this file the monaco editors language is javascript (see line 34) -->
<script src="./mylang.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React Monaco Editor Example</title>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/monaco-editor@0.25.2/min/vs/loader.js"></script>
</head>
<body>
<div id="root"></div>
<script>
// Define a React component that uses the Monaco editor
class MonacoEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorValue: "",
};
// Load the Monaco editor
require.config({ paths: { vs: "https://unpkg.com/monaco-editor@0.25.2/min/vs" } });
require(["vs/editor/editor.main"], () => {
// Initialize the editor
const editor = monaco.editor.create(document.getElementById("container"), {
value: "",
language: "mylang",
lineNumbers: false
});
});
}
render() {
return React.createElement(
'div',
null,
React.createElement('div', { id: 'container', style: { height: '400px', width: '800px' } }),
React.createElement('pre', null, this.state.editorValue)
);
}
}
// Render the MonacoEditor component to the root element
ReactDOM.render(React.createElement(MonacoEditor, null), document.getElementById("root"));
</script>
</body>
</html>and this is the beginning of my language definition file:
require(['vs/editor/editor.main'], function() {
// Define the language
monaco.languages.register({ id: 'mylang' });
// Define the list of keywords
const mylangKeywords = [
'continue', 'for', 'and', 'or',....If you are using the Monaco editor with create-react-app you will need a different approach, if you don't want to eject the app (to allow manually editing the webpack config file). This paragraph describes it pretty well:
The easiest way to use the react-monaco-editor with create-react-app is to use the react-app-rewired project. For setting it up, the following steps are required:
- Install react-app-rewired: npm install -D react-app-rewired
- Replace react-scripts by react-app-rewired in the scripts section of your packages.json
- Create a config-overrides.js in the root directory of your project with the following content:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = function override(config, env) { config.plugins.push(new MonacoWebpackPlugin({ languages: ['json'] })); return config; }For more information checkout the documentation of react-app-rewired here.
I did not have to specify anything else to make it work. No need to specify loaders for webpack manually.
For me both of the above answers are not working - not sure if it's related to Codesandbox or I did a mistake.
But using @monaco-editor/react is working with-out any changes to the CRA setup.
The only difference in the usage is that the default export is not a controlled component - so onchange is not working.
To have a controlled component, just use import {ControlledEditor as MonacoEditor} from "@monaco-editor/react". The onchange handler needs to be slightly modified, first the event & then the newText - just a small difference in the implementation.
The usage looks like following:
import React, { useState } from "react";
import { ControlledEditor as MonacoEditor } from "@monaco-editor/react";
export const Editor = () => {
const [code, setCode] = useState(`const greeting = () => {
alert("Hello world");
}`);
const options = {
minimap: {
enabled: false
}
};
const changeHandler = (evt, newText) => {
setCode(newText);
};
const editorDidMount = (editor, monaco) => {
console.log("editorDidMount", editor);
};
return (
<MonacoEditor
width="100%"
height="100%"
language="javascript"
theme="vs-dark"
value={code}
options={options}
onChange={changeHandler}
editorDidMount={editorDidMount}
/>
);
};
The options can be used to modify the Monaco editor. In my case I don't want to display the minimap. All available options can be found in the editor api docs
You can find the working demo in this Codesandbox.
The only thing that I found that is not working is undo/redo as described in the following issue. No change event triggered but I'll check this later - for now I'm happy with it.
You should be able to use the following code to configure an autocomplete that converts text typed in English to the fantasy language. As you're using React and TypeScript you might need to convert it slightly.
function createDependencyProposals(range) {
return [
{
documentation: 'beautiful',
insertText: 'byut',
kind: monaco.languages.CompletionItemKind.Keyword,
label: 'beautiful',
range: range
},
{
documentation: 'faster',
insertText: 'fsut',
kind: monaco.languages.CompletionItemKind.Keyword,
label: 'faster',
range: range
}
]
}
// Register the custom language
monaco.languages.register({id: 'bead'});
// Register the completion provider
monaco.languages.registerCompletionItemProvider('bead', {
provideCompletionItems: function (model, position) {
// Get the text before the cursor
var word = model.getWordUntilPosition(position);
var range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
};
// Compute the suggestions
return {
suggestions: createDependencyProposals(range),
};
}
});
monaco.editor.create(document.getElementById("container"), {
value: '',
language: "bead",
});
According to this, triggerCharacters are additional characters other than a-z that will also trigger suggestions (e.g. if you want to trigger suggestions when typing ., ! etc), so you won't need to define any explicit trigger characters if you just want to trigger suggestions when typing `a-z.
Here's a working Monaco Playground demo. You should be able to start typing the English word, e.g. type bea, the suggestion beautiful will appear, and if you select it (press Enter or click on it), it will insert the Bead word into the editor.
Seems like monaco won't trigger if your tokenizer is matching string, here:
export const languageDef = {
defaultToken: '',
tokenizer: {
root: [{ include: '@whitespace' }, { include: '@strings' }],
strings: [[/\w+/, 'string']],
whitespace: [[/\s+/, 'white']],
},
}
the @strings rule matches \w+ as soon as you type a char, marking it a string token class thus autocompletion will not trigger, you can fix it by changing string to identifier