Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins

License

NotificationsYou must be signed in to change notification settings

suren-atoyan/monaco-react

Repository files navigation

Monaco Editor for React · use themonaco-editor inanyReact application without needing to usewebpack (orrollup/parcel/etc) configuration files / plugins


  • ✅ ReactV19 support!
  • ⌨️ rewritten withTypeScript 🔥
  • multi-model editor is already supported; enjoy it 🎉
  • 🎉 versionv4 is here - to see what's new in the new version and how to migrate fromv3, please read thisdoc (also, if you need the old versionREADME, it'shere)
  • 🎮 the new sectionDevelopment / Playground has been created - now you can run the playground and play with the internals of the library
  • 💫 it's already integrated with@monaco-editor/loader

Synopsis

Monaco editor wrapper for easy/one-line integration with anyReact application without needing to usewebpack (or any other module bundler) configuration files / plugins. It can be used with apps generated bycreate-react-app,create-snowpack-app,vite,Next.js or any other app generators -you don't need to eject or rewire them.

Motivation

Themonaco-editor is a well-known web technology based code editor that powersVS Code. This library handles the setup process of themonaco-editor and provides a cleanAPI to interact withmonaco from anyReact environment

Demo

Check it out!

Documentation

Installation

npm install @monaco-editor/react# or @monaco-editor/react@next for React v19

or

yarn add @monaco-editor/react

or you can useCDN.Here is an example

NOTE: ForTypeScript type definitions, this package uses themonaco-editor package as a peer dependency. So, if you need types and don't already have themonaco-editor package installed, you will need to do so

Ask AI

Monaco-React AI will help you understand this repository better. You can ask for code examples, installation guide, debugging help and much more.

Introduction

Besides types, the library exportsEditorandDiffEditor components, as well as theloader utility and theuseMonaco hook:

importEditor,{DiffEditor,useMonaco,loader}from'@monaco-editor/react';

Usage

Simple usage

Here is an example of a simple integration ofmonaco editor with aReact project.
You just need to import and render theEditor component:

importReactfrom'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){return<Editorheight="90vh"defaultLanguage="javascript"defaultValue="// some comment"/>;}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

Extended example
importReactfrom'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){functionhandleEditorChange(value,event){// here is the current value}functionhandleEditorDidMount(editor,monaco){console.log('onMount: the editor instance:',editor);console.log('onMount: the monaco instance:',monaco);}functionhandleEditorWillMount(monaco){console.log('beforeMount: the monaco instance:',monaco);}functionhandleEditorValidation(markers){// model markers// markers.forEach(marker => console.log('onValidate:', marker.message));}return(<Editorheight="90vh"defaultLanguage="javascript"defaultValue="// some comment"onChange={handleEditorChange}onMount={handleEditorDidMount}beforeMount={handleEditorWillMount}onValidate={handleEditorValidation}/>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

Get value

There are two options to get the current value:

  1. get the current model value from theeditor instance
importReact,{useRef}from'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){consteditorRef=useRef(null);functionhandleEditorDidMount(editor,monaco){editorRef.current=editor;}functionshowValue(){alert(editorRef.current.getValue());}return(<><buttononClick={showValue}>Show value</button><Editorheight="90vh"defaultLanguage="javascript"defaultValue="// some comment"onMount={handleEditorDidMount}/></>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

  1. get the current model value viaonChange prop
importReactfrom'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){functionhandleEditorChange(value,event){console.log('here is the current model value:',value);}return(<Editorheight="90vh"defaultLanguage="javascript"defaultValue="// some comment"onChange={handleEditorChange}/>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

(get the `DiffEditor` values via `editor` instance)
importReact,{useRef}from'react';importReactDOMfrom'react-dom';import{DiffEditor}from'@monaco-editor/react';functionApp(){constdiffEditorRef=useRef(null);functionhandleEditorDidMount(editor,monaco){diffEditorRef.current=editor;}functionshowOriginalValue(){alert(diffEditorRef.current.getOriginalEditor().getValue());}functionshowModifiedValue(){alert(diffEditorRef.current.getModifiedEditor().getValue());}return(<><buttononClick={showOriginalValue}>show original value</button><buttononClick={showModifiedValue}>show modified value</button><DiffEditorheight="90vh"language="javascript"original="// the original code"modified="// the modified code"onMount={handleEditorDidMount}/></>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

editor instance

Theeditor instance is exposed from theonMount prop as a first parameter, the second is themonaco instance

importReact,{useRef}from'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){consteditorRef=useRef(null);functionhandleEditorDidMount(editor,monaco){// here is the editor instance// you can store it in `useRef` for further usageeditorRef.current=editor;}return(<Editorheight="90vh"defaultLanguage="javascript"defaultValue="// some comment"onMount={handleEditorDidMount}/>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

monaco instance

There are three options to get themonaco instance:

  1. viaonMount/beforeMount
importReact,{useRef}from'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){constmonacoRef=useRef(null);functionhandleEditorWillMount(monaco){// here is the monaco instance// do something before editor is mountedmonaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);}functionhandleEditorDidMount(editor,monaco){// here is another way to get monaco instance// you can also store it in `useRef` for further usagemonacoRef.current=monaco;}return(<Editorheight="90vh"defaultLanguage="javascript"defaultValue="// some comment"beforeMount={handleEditorWillMount}onMount={handleEditorDidMount}/>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

  1. vialoader utility
import{loader}from'@monaco-editor/react';loader.init().then((monaco)=>console.log('here is the monaco instance:',monaco));

codesandbox

  1. viauseMonaco hook
importReactfrom'react';importReactDOMfrom'react-dom';importEditor,{useMonaco}from'@monaco-editor/react';functionApp(){constmonaco=useMonaco();useEffect(()=>{if(monaco){console.log('here is the monaco instance:',monaco);}},[monaco]);return<Editorheight="90vh"defaultValue="// some comment"defaultLanguage="javascript"/>;}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

useMonaco

useMonaco is aReact hook that returns the instance of themonaco. But there is an important note that should be considered: the initialization process is being handled by theloader utility (the reference of@monaco-editor/loader): that process is being done asynchronously and only once. So, if the first initiator of the initialization isuseMonaco hook, the first returned value will be null, due to its asynchronous installation. Just check the returned value ofuseMonaco

importReact,{useEffect}from'react';importReactDOMfrom'react-dom';importEditor,{useMonaco}from'@monaco-editor/react';functionApp(){constmonaco=useMonaco();useEffect(()=>{// do conditional chainingmonaco?.languages.typescript.javascriptDefaults.setEagerModelSync(true);// or make sure that it exists by other waysif(monaco){console.log('here is the monaco instance:',monaco);}},[monaco]);return<Editorheight="90vh"defaultValue="// some comment"defaultLanguage="javascript"/>;}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

loader-config

The library exports (named) the utility calledloader. Basically, it's the reference of@monaco-editor/loader. By default,monaco files are being downloaded fromCDN. There is an ability to change this behavior, and other things concerning theAMD loader ofmonaco. We have a defaultconfig file that you can modify by the way shown below:

import{loader}from'@monaco-editor/react';// you can change the source of the monaco filesloader.config({paths:{vs:'...'}});// you can configure the localesloader.config({'vs/nls':{availableLanguages:{'*':'de'}}});// orloader.config({paths:{vs:'...',},'vs/nls':{availableLanguages:{'*':'de',},},});
usemonaco-editor as an npm package

Starting from versionv4.4.0 it's possible to usemonaco-editor as annpm package; import it fromnode_modules and includemonaco sources into your bundle (instead of using CDN). To make it work you can do the following:

import*asmonacofrom'monaco-editor';import{loader}from'@monaco-editor/react';loader.config({ monaco});// ...

NOTE: you should be aware that this may require additionalwebpack plugins, likemonaco-editor-webpack-plugin or it may be impossible to use in apps generated byCRA without ejecting them.

If you useVite, you need to do this:

import{loader}from'@monaco-editor/react';import*asmonacofrom'monaco-editor';importeditorWorkerfrom'monaco-editor/esm/vs/editor/editor.worker?worker';importjsonWorkerfrom'monaco-editor/esm/vs/language/json/json.worker?worker';importcssWorkerfrom'monaco-editor/esm/vs/language/css/css.worker?worker';importhtmlWorkerfrom'monaco-editor/esm/vs/language/html/html.worker?worker';importtsWorkerfrom'monaco-editor/esm/vs/language/typescript/ts.worker?worker';self.MonacoEnvironment={getWorker(_,label){if(label==='json'){returnnewjsonWorker();}if(label==='css'||label==='scss'||label==='less'){returnnewcssWorker();}if(label==='html'||label==='handlebars'||label==='razor'){returnnewhtmlWorker();}if(label==='typescript'||label==='javascript'){returnnewtsWorker();}returnneweditorWorker();},};loader.config({ monaco});loader.init().then(/* ... */);

codesandbox

NOTE: your passed object will be deeply merged with thedefault one

Multi-model editor

When you render theEditor component, a default model is being created. It's important to mention that when you change thelanguage orvalue 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 inIDEs. And previously to handle multiple models they had to do it manually and out of the component. Now, the multi-modelAPI is supported 🎉 Let's check how it works. There are three parameters to create a model -value,language andpath (monaco.editor.createModel(value, language, monaco.Uri.parse(path))). You can consider last one (path) as an identifier for the model. TheEditor component, now, has apath prop. When you specify apath prop, theEditor 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 aJSON like representation of some file structure, something like this:

constfiles={'script.js':{name:'script.js',language:'javascript',value:someJSCodeExample,},'style.css':{name:'style.css',language:'css',value:someCSSCodeExample,},'index.html':{name:'index.html',language:'html',value:someHTMLCodeExample,},};

And here is our simple multi-model editor implementation:

importReactfrom'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){const[fileName,setFileName]=useState('script.js');constfile=files[fileName];return(<><buttondisabled={fileName==='script.js'}onClick={()=>setFileName('script.js')}>        script.js</button><buttondisabled={fileName==='style.css'}onClick={()=>setFileName('style.css')}>        style.css</button><buttondisabled={fileName==='index.html'}onClick={()=>setFileName('index.html')}>        index.html</button><Editorheight="80vh"theme="vs-dark"path={file.name}defaultLanguage={file.language}defaultValue={file.value}/></>);}constrootElement=document.getElementById('root');ReactDOM.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, anddefaultPath are being consideredonly during a new model creation
value,language, andpath are being tracked thewhole time
saveViewState is an indicator whether to save the models' view states between model changes or not

codesandbox

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

importReactfrom'react';importReactDOMfrom'react-dom';importEditorfrom'@monaco-editor/react';functionApp(){functionhandleEditorValidation(markers){// model markersmarkers.forEach((marker)=>console.log('onValidate:',marker.message));}return(<Editorheight="90vh"defaultLanguage="javascript"defaultValue="// let's write some broken code 😈"onValidate={handleEditorValidation}/>);}constrootElement=document.getElementById('root');ReactDOM.render(<App/>,rootElement);

codesandbox

It's important to mention that according tomonaco-editor, the whole supported languages are divided into two groups:

  1. languages that have richIntelliSense and validation
  • TypeScript
  • JavaScript
  • CSS
  • LESS
  • SCSS
  • JSON
  • HTML
  1. languages with only basic syntax colorization
  • 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

Notes

Forelectron users

As a usualReact component, this one also works fine with an electron-react environment, without need to have awebpack configuration or other extra things. But there are several cases that developers usually face to and sometimes it can be confusing. Here they are:

  1. You see loading screen stuckUsually, it's because your environment doesn't allow you to load external sources. By default, it loadsmonaco sources fromCDN. You can see thedefault configuration. But sure you can change that behavior; the library is fully configurable. Read about ithere. So, if you want to download it from your local files, you can do it like this:
import{loader}from'@monaco-editor/react';loader.config({paths:{vs:'../path-to-monaco'}});
  1. Based on your electron environment it can be required to have an absolute URLThe utility function taken fromhere can help you to achieve that. Let's imagine you havemonaco-editor package installed and you want to load monaco from thenode_modules rather than from CDN: in that case, you can write something like this:
functionensureFirstBackSlash(str){returnstr.length>0&&str.charAt(0)!=='/' ?'/'+str :str;}functionuriFromPath(_path){constpathName=path.resolve(_path).replace(/\\/g,'/');returnencodeURI('file://'+ensureFirstBackSlash(pathName));}loader.config({paths:{vs:uriFromPath(path.join(__dirname,'../node_modules/monaco-editor/min/vs')),},});

There were several issues about this topic that can be helpful too -1234

Also, there is ablog post about using@monaco-editor/react inElectron in offline mode. You may find it helpful.

And if you useelectron withmonaco andreact and have faced an issue different than the above-discribed ones, please let us know to make this section more helpful

ForNext.js users

Like other React components, this one also works withNext.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, tomonaco instance you should be aware that it wants to access thedocument 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 describedhere

And if you usemonaco withNext.js and have faced an issue different than the above-described one, please let us know to make this section more helpful

Create your own editor

Under the hood this library uses@monaco-editor/loader that provides a utility calledloader. Theloader utility is a collection of functions that are being used to setupmonaco editor into your browser.loader.init() handles the whole initialization process and returns the instance of themonaco -loader.init().then(monaco => console.log("here is the monaco instance:", monaco)). TheEditor component uses this utility, gains access tomonaco instance and creates the editor.Here is the implementation of theEditor component. You can use the same technique to create your ownEditor. You can just import theloader utility, access tomonaco instance, and create your own editor with your own custom logic. The shortest way to do it:

importloaderfrom'@monaco-editor/loader';loader.init().then((monaco)=>{constwrapper=document.getElementById('root');wrapper.style.height='100vh';constproperties={value:'function hello() {\n\talert("Hello world!");\n}',language:'javascript',};monaco.editor.create(wrapper,properties);});

That's all. You can wrap it into aReact component, orVue, orAngular or leave it as vanilla one or whatever you want; it's written in purejs

codesandbox

Development-Playground

It's always important to have a place, where you can play with the internals of the library. Theplayground is a minimalReact app that directly uses the sources of the library. So, if you are going to open aPR, or want to check something, or just want to try the freshest state of the library, you can run the playground and enjoy it

  • clone the repository
git clone https://github.com/suren-atoyan/monaco-react.git
  • go to the library folder
cd monaco-react
  • install the library's dependencies
 npm install# yarn
  • go to the playground
cd playground
  • install the playground's dependencies
npm install# yarn
  • and run the playground
npm run dev# yarn dev
monaco-react├── playground│   ├── src/      # playground sources├── src/          # library sources└── ...

If you want to change something in the library, go tomonaco-react/src/..., the library will be automatically re-built and the playground will use the latest build

Props

Editor

NameTypeDefaultDescription
defaultValuestringDefault value of the current model
defaultLanguagestringDefault language of the current model
defaultPathstringDefault path of the current model. Will be passed as the third argument to.createModel method -monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath))
valuestringValue of the current model
languageenum: ...Language of the current model (all languages that aresupported by monaco-editor)
pathstringPath of the current model. Will be passed as the third argument to.createModel method -monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath))
themeenum: "light" | "vs-dark""light"The theme for the monaco. Available options "vs-dark" | "light". Define new themes bymonaco.editor.defineTheme
linenumberThe line to jump on it
loadingReact Node"Loading..."The loading screen before the editor will be mounted
optionsobject{}IStandaloneEditorConstructionOptions
overrideServicesobject{}IEditorOverrideServices
saveViewStatebooleantrueIndicator whether to save the models' view states between model changes or not
keepCurrentModelbooleanfalseIndicator whether to dispose the current model when the Editor is unmounted or not
widthunion: number | string"100%"Width of the editor wrapper
heightunion: number | string"100%"Height of the editor wrapper
classNamestringClass name for the editor container
wrapperPropsobject{}Props applied to the wrapper element
beforeMountfuncnoopSignature: function(monaco: Monaco) => void
An event is emitted before the editor is mounted. It gets themonaco instance as a first argument
onMountfuncnoopSignature: function(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => void
An event is emitted when the editor is mounted. It gets theeditor instance as a first argument and themonaco instance as a second
onChangefuncSignature: function(value: string | undefined, ev: monaco.editor.IModelContentChangedEvent) => void
An event is emitted when the content of the current model is changed
onValidatefuncnoopSignature: 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

NameTypeDefaultDescription
originalstringThe original source (left one) value
modifiedstringThe modified source (right one) value
languageenum: ...Language for the both models - original and modified (all languages that aresupported by monaco-editor)
originalLanguageenum: ...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
modifiedLanguageenum: ...This prop gives you the opportunity to specify the language of the modified source separately, otherwise, it will get the value of language property
originalModelPathstringPath for the "original" model. Will be passed as a third argument to.createModel method -monaco.editor.createModel(..., ..., monaco.Uri.parse(originalModelPath))
modifiedModelPathstringPath for the "modified" model. Will be passed as a third argument to.createModel method -monaco.editor.createModel(..., ..., monaco.Uri.parse(modifiedModelPath))
keepCurrentOriginalModelbooleanfalseIndicator whether to dispose the current original model when the DiffEditor is unmounted or not
keepCurrentModifiedModelbooleanfalseIndicator whether to dispose the current modified model when the DiffEditor is unmounted or not
themeenum: "light" | "vs-dark""light"The theme for the monaco. Available options "vs-dark" | "light". Define new themes bymonaco.editor.defineTheme
linenumberThe line to jump on it
loadingReact Node"Loading..."The loading screen before the editor will be mounted
optionsobject{}IDiffEditorConstructionOptions
widthunion: number | string"100%"Width of the editor wrapper
heightunion: number | string"100%"Height of the editor wrapper
classNamestringClass name for the editor container
wrapperPropsobject{}Props applied to the wrapper element
beforeMountfuncnoopSignature: function(monaco: Monaco) => void
An event is emitted before the editor mounted. It gets themonaco instance as a first argument
onMountfuncnoopSignature: function(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => void
An event is emitted when the editor is mounted. It gets theeditor instance as a first argument and themonaco instance as a second

License

MIT

About

Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

  •  

Contributors40


[8]ページ先頭

©2009-2025 Movatter.jp