Gathering detailed insights and metrics for @monaco-editor/react
Gathering detailed insights and metrics for @monaco-editor/react
Gathering detailed insights and metrics for @monaco-editor/react
Gathering detailed insights and metrics for @monaco-editor/react
@codingame/monaco-editor-react
Monaco Editor React component
@codingame/monaco-languageclient-react
Monaco Editor React component
@hitachivantara/uikit-react-code-editor
A wrapper to the React Monaco editor (https://github.com/react-monaco-editor/react-monaco-editor) using the Hitachi Vantara's Design System styles.
@opencreaters/monaco-editor-react
Monaco Editor React component
Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins
npm install @monaco-editor/react
52.7
Supply Chain
88.2
Quality
76.1
Maintenance
100
Vulnerability
99.6
License
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
3,812 Stars
441 Commits
268 Forks
19 Watching
6 Branches
40 Contributors
Updated on 27 Nov 2024
Minified
Minified + Gzipped
TypeScript (97.73%)
CSS (1.25%)
HTML (1.02%)
Cumulative downloads
Total Downloads
Last day
-10.7%
147,559
Compared to previous day
Last week
1.7%
836,622
Compared to previous week
Last month
14.5%
3,550,390
Compared to previous month
Last year
77.6%
32,866,571
Compared to previous year
1
3
Monaco Editor for React · use the monaco-editor in any React application without needing to use webpack
(or rollup
/parcel
/etc) configuration files / plugins
TypeScript
:fire:v4
is here - to see what's new in the new version and how to migrate from v3
, please read this doc (also, if you need the old version README
, it's here)Monaco
editor wrapper for easy/one-line integration with any React
application without needing to use webpack
(or any other module bundler) configuration files / plugins. It can be used with apps generated by create-react-app
, create-snowpack-app
, vite
, Next.js
or any other app generators - you don't need to eject or rewire them.
The monaco-editor is a well-known web technology based code editor that powers VS Code. This library handles the setup process of the monaco-editor
and provides a clean API
to interact with monaco
from any React
environment
1npm install @monaco-editor/react
or
1yarn add @monaco-editor/react
or you can use CDN
. Here is an example
NOTE: For TypeScript
type definitions, this package uses the monaco-editor package as a peer dependency. So, if you need types and don't already have the monaco-editor package installed, you will need to do so
Besides types, the library exports Editor
and DiffEditor
components, as well as the loader
utility and the useMonaco
hook:
1import Editor, { DiffEditor, useMonaco, loader } from '@monaco-editor/react';
Here is an example of a simple integration of monaco
editor with a React
project.
You just need to import and render the Editor
component:
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 return <Editor height="90vh" defaultLanguage="javascript" defaultValue="// some comment" />; 8} 9 10const rootElement = document.getElementById('root'); 11ReactDOM.render(<App />, rootElement);
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 function handleEditorChange(value, event) { 8 // here is the current value 9 } 10 11 function handleEditorDidMount(editor, monaco) { 12 console.log('onMount: the editor instance:', editor); 13 console.log('onMount: the monaco instance:', monaco); 14 } 15 16 function handleEditorWillMount(monaco) { 17 console.log('beforeMount: the monaco instance:', monaco); 18 } 19 20 function handleEditorValidation(markers) { 21 // model markers 22 // markers.forEach(marker => console.log('onValidate:', marker.message)); 23 } 24 25 return ( 26 <Editor 27 height="90vh" 28 defaultLanguage="javascript" 29 defaultValue="// some comment" 30 onChange={handleEditorChange} 31 onMount={handleEditorDidMount} 32 beforeMount={handleEditorWillMount} 33 onValidate={handleEditorValidation} 34 /> 35 ); 36} 37 38const rootElement = document.getElementById('root'); 39ReactDOM.render(<App />, rootElement);
There are two options to get the current value:
editor
instance1import React, { useRef } from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 const editorRef = useRef(null); 8 9 function handleEditorDidMount(editor, monaco) { 10 editorRef.current = editor; 11 } 12 13 function showValue() { 14 alert(editorRef.current.getValue()); 15 } 16 17 return ( 18 <> 19 <button onClick={showValue}>Show value</button> 20 <Editor 21 height="90vh" 22 defaultLanguage="javascript" 23 defaultValue="// some comment" 24 onMount={handleEditorDidMount} 25 /> 26 </> 27 ); 28} 29 30const rootElement = document.getElementById('root'); 31ReactDOM.render(<App />, rootElement);
onChange
prop1import React from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 function handleEditorChange(value, event) { 8 console.log('here is the current model value:', value); 9 } 10 11 return ( 12 <Editor 13 height="90vh" 14 defaultLanguage="javascript" 15 defaultValue="// some comment" 16 onChange={handleEditorChange} 17 /> 18 ); 19} 20 21const rootElement = document.getElementById('root'); 22ReactDOM.render(<App />, rootElement);
1import React, { useRef } from 'react'; 2import ReactDOM from 'react-dom'; 3 4import { DiffEditor } from '@monaco-editor/react'; 5 6function App() { 7 const diffEditorRef = useRef(null); 8 9 function handleEditorDidMount(editor, monaco) { 10 diffEditorRef.current = editor; 11 } 12 13 function showOriginalValue() { 14 alert(diffEditorRef.current.getOriginalEditor().getValue()); 15 } 16 17 function showModifiedValue() { 18 alert(diffEditorRef.current.getModifiedEditor().getValue()); 19 } 20 21 return ( 22 <> 23 <button onClick={showOriginalValue}>show original value</button> 24 <button onClick={showModifiedValue}>show modified value</button> 25 <DiffEditor 26 height="90vh" 27 language="javascript" 28 original="// the original code" 29 modified="// the modified code" 30 onMount={handleEditorDidMount} 31 /> 32 </> 33 ); 34} 35 36const rootElement = document.getElementById('root'); 37ReactDOM.render(<App />, rootElement);
editor instance
The editor
instance is exposed from the onMount
prop as a first parameter, the second is the monaco
instance
1import React, { useRef } from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 const editorRef = useRef(null); 8 9 function handleEditorDidMount(editor, monaco) { 10 // here is the editor instance 11 // you can store it in `useRef` for further usage 12 editorRef.current = editor; 13 } 14 15 return ( 16 <Editor 17 height="90vh" 18 defaultLanguage="javascript" 19 defaultValue="// some comment" 20 onMount={handleEditorDidMount} 21 /> 22 ); 23} 24 25const rootElement = document.getElementById('root'); 26ReactDOM.render(<App />, rootElement);
monaco instance
There are three options to get the monaco
instance:
onMount/beforeMount
1import React, { useRef } from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 const monacoRef = useRef(null); 8 9 function handleEditorWillMount(monaco) { 10 // here is the monaco instance 11 // do something before editor is mounted 12 monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true); 13 } 14 15 function handleEditorDidMount(editor, monaco) { 16 // here is another way to get monaco instance 17 // you can also store it in `useRef` for further usage 18 monacoRef.current = monaco; 19 } 20 21 return ( 22 <Editor 23 height="90vh" 24 defaultLanguage="javascript" 25 defaultValue="// some comment" 26 beforeMount={handleEditorWillMount} 27 onMount={handleEditorDidMount} 28 /> 29 ); 30} 31 32const rootElement = document.getElementById('root'); 33ReactDOM.render(<App />, rootElement);
loader
utility1import { loader } from '@monaco-editor/react'; 2 3loader.init().then((monaco) => console.log('here is the monaco instance:', monaco));
useMonaco
hook1import React from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor, { useMonaco } from '@monaco-editor/react'; 5 6function App() { 7 const monaco = useMonaco(); 8 9 useEffect(() => { 10 if (monaco) { 11 console.log('here is the monaco instance:', monaco); 12 } 13 }, [monaco]); 14 15 return <Editor height="90vh" defaultValue="// some comment" defaultLanguage="javascript" />; 16} 17 18const rootElement = document.getElementById('root'); 19ReactDOM.render(<App />, rootElement);
useMonaco
useMonaco
is a React
hook that returns the instance of the monaco
. But there is an important note that should be considered: the initialization process is being handled by the loader
utility (the reference of @monaco-editor/loader): that process is being done asynchronously and only once. So, if the first initiator of the initialization is useMonaco
hook, the first returned value will be null, due to its asynchronous installation. Just check the returned value of useMonaco
1import React, { useEffect } from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor, { useMonaco } from '@monaco-editor/react'; 5 6function App() { 7 const monaco = useMonaco(); 8 9 useEffect(() => { 10 // do conditional chaining 11 monaco?.languages.typescript.javascriptDefaults.setEagerModelSync(true); 12 // or make sure that it exists by other ways 13 if (monaco) { 14 console.log('here is the monaco instance:', monaco); 15 } 16 }, [monaco]); 17 18 return <Editor height="90vh" defaultValue="// some comment" defaultLanguage="javascript" />; 19} 20 21const rootElement = document.getElementById('root'); 22ReactDOM.render(<App />, rootElement);
loader-config
The library exports (named) the utility called loader
. Basically, it's the reference of @monaco-editor/loader. By default, monaco
files are being downloaded from CDN
. There is an ability to change this behavior, and other things concerning the AMD
loader of monaco
. We have a default config file that you can modify by the way shown below:
1import { loader } from '@monaco-editor/react'; 2 3// you can change the source of the monaco files 4loader.config({ paths: { vs: '...' } }); 5 6// you can configure the locales 7loader.config({ 'vs/nls': { availableLanguages: { '*': 'de' } } }); 8 9// or 10loader.config({ 11 paths: { 12 vs: '...', 13 }, 14 'vs/nls': { 15 availableLanguages: { 16 '*': 'de', 17 }, 18 }, 19});
monaco-editor
as an npm packageStarting from version v4.4.0
it's possible to use monaco-editor
as an npm
package; import it from node_modules
and include monaco
sources into your bundle (instead of using CDN). To make it work you can do the following:
1import * as monaco from 'monaco-editor'; 2import { loader } from '@monaco-editor/react'; 3 4loader.config({ monaco }); 5 6// ...
NOTE: you should be aware that this may require additional webpack
plugins, like monaco-editor-webpack-plugin or it may be impossible to use in apps generated by CRA without ejecting them.
If you use Vite, you need to do this:
1import { loader } from '@monaco-editor/react'; 2 3import * as monaco from 'monaco-editor'; 4import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; 5import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; 6import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; 7import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; 8import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; 9 10self.MonacoEnvironment = { 11 getWorker(_, label) { 12 if (label === 'json') { 13 return new jsonWorker(); 14 } 15 if (label === 'css' || label === 'scss' || label === 'less') { 16 return new cssWorker(); 17 } 18 if (label === 'html' || label === 'handlebars' || label === 'razor') { 19 return new htmlWorker(); 20 } 21 if (label === 'typescript' || label === 'javascript') { 22 return new tsWorker(); 23 } 24 return new editorWorker(); 25 }, 26}; 27 28loader.config({ monaco }); 29 30loader.init().then(/* ... */);
NOTE: your passed object will be deeply merged with the default one
When you render the Editor
component, a default model is being created. It's important to mention that when you change the language
or value
props, they affect the same model that has been auto-created at the mount of the component. In most cases it's okay, but the developers face problems when they want to implement a multi-model editor to support tabs/files like in IDE
s. And previously to handle multiple models they had to do it manually and out of the component. Now, the multi-model API
is supported :tada: Let's check how it works. There are three parameters to create a model - value
, language
and path
(monaco.editor.createModel(value, language, monaco.Uri.parse(path))
). You can consider last one (path
) as an identifier for the model. The Editor
component, now, has a path
prop. When you specify a path
prop, the Editor
component checks if it has a model by that path or not. If yes, the existing model will be shown, otherwise, a new one will be created (and stored). Using this technique you can correspond your files with paths, and create a fully multi-model editor. You can open your file, do some changes, choose another file, and when you come back to the first one the previous model will be shown with the whole view state, text selection, undo stack, scroll position, etc. (simple demo)
Here is a simple example: let's imagine we have a JSON
like representation of some file structure, something like this:
1const files = { 2 'script.js': { 3 name: 'script.js', 4 language: 'javascript', 5 value: someJSCodeExample, 6 }, 7 'style.css': { 8 name: 'style.css', 9 language: 'css', 10 value: someCSSCodeExample, 11 }, 12 'index.html': { 13 name: 'index.html', 14 language: 'html', 15 value: someHTMLCodeExample, 16 }, 17};
And here is our simple multi-model editor implementation:
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 const [fileName, setFileName] = useState('script.js'); 8 9 const file = files[fileName]; 10 11 return ( 12 <> 13 <button disabled={fileName === 'script.js'} onClick={() => setFileName('script.js')}> 14 script.js 15 </button> 16 <button disabled={fileName === 'style.css'} onClick={() => setFileName('style.css')}> 17 style.css 18 </button> 19 <button disabled={fileName === 'index.html'} onClick={() => setFileName('index.html')}> 20 index.html 21 </button> 22 <Editor 23 height="80vh" 24 theme="vs-dark" 25 path={file.name} 26 defaultLanguage={file.language} 27 defaultValue={file.value} 28 /> 29 </> 30 ); 31} 32 33const rootElement = document.getElementById('root'); 34ReactDOM.render(<App />, rootElement);
The properties:
defaultValue
defaultLanguage
defaultPath
value
language
path
saveViewState
will give you more flexibility in working with a multi-model editor.
NOTE
defaultValue
, defaultLanguage
, and defaultPath
are being considered only during a new model creation
value
, language
, and path
are being tracked the whole time
saveViewState
is an indicator whether to save the models' view states between model changes or not
onValidate
onValidate
is an additional property. An event is emitted when the content of the current model is changed and the current model markers are ready. It will be fired with the current model markers
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3 4import Editor from '@monaco-editor/react'; 5 6function App() { 7 function handleEditorValidation(markers) { 8 // model markers 9 markers.forEach((marker) => console.log('onValidate:', marker.message)); 10 } 11 12 return ( 13 <Editor 14 height="90vh" 15 defaultLanguage="javascript" 16 defaultValue="// let's write some broken code 😈" 17 onValidate={handleEditorValidation} 18 /> 19 ); 20} 21 22const rootElement = document.getElementById('root'); 23ReactDOM.render(<App />, rootElement);
It's important to mention that according to monaco-editor, the whole supported languages are divided into two groups:
IntelliSense
and validationTypeScript
JavaScript
CSS
LESS
SCSS
JSON
HTML
XML
PHP
C#
C++
Razor
Markdown
Diff
Java
VB
CoffeeScript
Handlebars
Batch
Pug
F#
Lua
Powershell
Python
Ruby
SASS
R
Objective-C
As you can guess, onValidate
prop will work only with the languages from the first group
electron
usersAs a usual React
component, this one also works fine with an electron-react environment, without need to have a webpack
configuration or other extra things. But there are several cases that developers usually face to and sometimes it can be confusing. Here they are:
monaco
sources from CDN
. You can see the default configuration. But sure you can change that behavior; the library is fully configurable. Read about it here. So, if you want to download it from your local files, you can do it like this:1import { loader } from '@monaco-editor/react'; 2 3loader.config({ paths: { vs: '../path-to-monaco' } });
monaco-editor
package installed and you want to load monaco from the node_modules
rather than from CDN: in that case, you can write something like this:1function ensureFirstBackSlash(str) { 2 return str.length > 0 && str.charAt(0) !== '/' ? '/' + str : str; 3} 4 5function uriFromPath(_path) { 6 const pathName = path.resolve(_path).replace(/\\/g, '/'); 7 return encodeURI('file://' + ensureFirstBackSlash(pathName)); 8} 9 10loader.config({ 11 paths: { 12 vs: uriFromPath(path.join(__dirname, '../node_modules/monaco-editor/min/vs')), 13 }, 14});
There were several issues about this topic that can be helpful too - 1 2 3 4
Also, there is a blog post about using @monaco-editor/react
in Electron
in offline mode. You may find it helpful.
And if you use electron
with monaco
and react
and have faced an issue different than the above-discribed ones, please let us know to make this section more helpful
Next.js
usersLike other React components, this one also works with Next.js
without a hitch. The part of the source that should be pre-parsed is optimized for server-side rendering, so, in usual cases, it will work fine, but if you want to have access, for example, to monaco instance
you should be aware that it wants to access the document
object, and it requires browser environment. Basically you just need to avoid running that part out of browser environment, there are several ways to do that. The one is described here
And if you use monaco
with Next.js
and have faced an issue different than the above-described one, please let us know to make this section more helpful
Under the hood this library uses @monaco-editor/loader that provides a utility called loader
. The loader
utility is a collection of functions that are being used to setup monaco
editor into your browser. loader.init()
handles the whole initialization process and returns the instance of the monaco
- loader.init().then(monaco => console.log("here is the monaco instance:", monaco))
. The Editor
component uses this utility, gains access to monaco instance
and creates the editor. Here is the implementation of the Editor
component. You can use the same technique to create your own Editor
. You can just import the loader
utility, access to monaco instance
, and create your own editor with your own custom logic. The shortest way to do it:
1import loader from '@monaco-editor/loader'; 2 3loader.init().then((monaco) => { 4 const wrapper = document.getElementById('root'); 5 wrapper.style.height = '100vh'; 6 const properties = { 7 value: 'function hello() {\n\talert("Hello world!");\n}', 8 language: 'javascript', 9 }; 10 11 monaco.editor.create(wrapper, properties); 12});
That's all. You can wrap it into a React
component, or Vue
, or Angular
or leave it as vanilla one or whatever you want; it's written in pure js
It's always important to have a place, where you can play with the internals of the library. The playground
is a minimal React
app that directly uses the sources of the library. So, if you are going to open a PR
, or want to check something, or just want to try the freshest state of the library, you can run the playground and enjoy it
1git clone https://github.com/suren-atoyan/monaco-react.git
1 cd monaco-react
1 npm install # yarn
1 cd playground
1npm install # yarn
1npm run dev # yarn dev
monaco-react
├── playground
│ ├── src/ # playground sources
├── src/ # library sources
└── ...
If you want to change something in the library, go to monaco-react/src/...
, the library will be automatically re-built and the playground will use the latest build
Editor
Name | Type | Default | Description |
---|---|---|---|
defaultValue | string | Default value of the current model | |
defaultLanguage | string | Default language of the current model | |
defaultPath | string | Default path of the current model. Will be passed as the third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath)) | |
value | string | Value of the current model | |
language | enum: ... | Language of the current model (all languages that are supported by monaco-editor) | |
path | string | Path of the current model. Will be passed as the third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath)) | |
theme | enum: "light" | "vs-dark" | "light" | The theme for the monaco. Available options "vs-dark" | "light". Define new themes by monaco.editor.defineTheme |
line | number | The line to jump on it | |
loading | React Node | "Loading..." | The loading screen before the editor will be mounted |
options | object | {} | IStandaloneEditorConstructionOptions |
overrideServices | object | {} | IEditorOverrideServices |
saveViewState | boolean | true | Indicator whether to save the models' view states between model changes or not |
keepCurrentModel | boolean | false | Indicator whether to dispose the current model when the Editor is unmounted or not |
width | union: number | string | "100%" | Width of the editor wrapper |
height | union: number | string | "100%" | Height of the editor wrapper |
className | string | Class name for the editor container | |
wrapperProps | object | {} | Props applied to the wrapper element |
beforeMount | func | noop | Signature: function(monaco: Monaco) => void An event is emitted before the editor is mounted. It gets the monaco instance as a first argument |
onMount | func | noop | Signature: function(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => void An event is emitted when the editor is mounted. It gets the editor instance as a first argument and the monaco instance as a second |
onChange | func | Signature: function(value: string | undefined, ev: monaco.editor.IModelContentChangedEvent) => void An event is emitted when the content of the current model is changed | |
onValidate | func | noop | Signature: function(markers: monaco.editor.IMarker[]) => void An event is emitted when the content of the current model is changed and the current model markers are ready |
DiffEditor
Name | Type | Default | Description |
---|---|---|---|
original | string | The original source (left one) value | |
modified | string | The modified source (right one) value | |
language | enum: ... | Language for the both models - original and modified (all languages that are supported by monaco-editor) | |
originalLanguage | enum: ... | This prop gives you the opportunity to specify the language of the original source separately, otherwise, it will get the value of the language property | |
modifiedLanguage | enum: ... | This prop gives you the opportunity to specify the language of the modified source separately, otherwise, it will get the value of language property | |
originalModelPath | string | Path for the "original" model. Will be passed as a third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(originalModelPath)) | |
modifiedModelPath | string | Path for the "modified" model. Will be passed as a third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(modifiedModelPath)) | |
keepCurrentOriginalModel | boolean | false | Indicator whether to dispose the current original model when the DiffEditor is unmounted or not |
keepCurrentModifiedModel | boolean | false | Indicator whether to dispose the current modified model when the DiffEditor is unmounted or not |
theme | enum: "light" | "vs-dark" | "light" | The theme for the monaco. Available options "vs-dark" | "light". Define new themes by monaco.editor.defineTheme |
line | number | The line to jump on it | |
loading | React Node | "Loading..." | The loading screen before the editor will be mounted |
options | object | {} | IDiffEditorConstructionOptions |
width | union: number | string | "100%" | Width of the editor wrapper |
height | union: number | string | "100%" | Height of the editor wrapper |
className | string | Class name for the editor container | |
wrapperProps | object | {} | Props applied to the wrapper element |
beforeMount | func | noop | Signature: function(monaco: Monaco) => void An event is emitted before the editor mounted. It gets the monaco instance as a first argument |
onMount | func | noop | Signature: function(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => void An event is emitted when the editor is mounted. It gets the editor instance as a first argument and the monaco instance as a second |
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
0 commit(s) and 5 issue activity found in the last 90 days -- score normalized to 4
Reason
Found 5/30 approved changesets -- score normalized to 1
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
105 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More