Build a web editor with react-monaco-editor
Tags: react, webdev
Published: 2023-05-04 14:39:58

**Written by [Isaac Okoro](https://blog.logrocket.com/author/isaacjunior/)✏️**

Online web editors are web-based applications that allow users to write and edit code on a browser. They work without needing local environments or setups, encouraging accessibility, collaboration, and simplicity. 

Online editors offer a simple interface for writing and editing code and time-saving tools like syntax highlighting, line numbering, and auto-completion. Some examples of online web editors include [Repl.it](https://replit.com/), [CodePen](https://codepen.io/), and [CodeSandbox](https://codesandbox.io/). In this article, we will cover how to build a web editor with React and the [react-monaco-editor](https://www.npmjs.com/package/react-monaco-editor?activeTab=readme) package. 

_Jump ahead:_

*   [What is Monaco Editor?](#monaco-editor)
    *   [react-monaco-editor vs. @monaco-editor/react](#react-monaco-editor-vs-@monaco-editor/react)
*   [Creating a new app and installing dependencies](#creating-new-app-installing-dependencies)
    *   [Rewiring the React app to work with Monaco Editor](#rewiring-react-app-work-with-monaco-editor)
*   [Building the code editor in React](#building-code-editor-react)
    *   [Passing `options` to the editor](#passing-options-editor)
    *   [Opening files in the editor](#opening-files-editor)
    *   [Adding `themes` to the code editor](#adding-themes-code-editor)
    *   [Creating a `Diff` editor with the Monaco Editor](#creating-diff-editor-with-monaco-editor)

## What is Monaco Editor? <a name="monaco-editor">

Monaco Editor is a browser-based code editor developed by Microsoft and is used in several of Microsoft's products, including VS Code, Azure DevOps, and Visual Studio Online. The Monaco Editor is designed to provide a fast and efficient coding experience, with features like syntax highlighting and auto-completion. It also has inbuilt support for various programming languages and technologies, including [JavaScript](https://blog.logrocket.com/tag/vanilla-javascript/), [TypeScript](https://blog.logrocket.com/tag/typescript/), [HTML](https://blog.logrocket.com/tag/html/), [CSS](https://blog.logrocket.com/tag/css/), and more. 

Two packages are available for using React with Monaco Editor: react-monaco-editor and @monaco-editor/react. React-monaco-editor was created six years ago with over 114,000 weekly npm downloads, while the @monaco-editor/react package was designed four years ago and had over 380,000 weekly npm downloads.

### react-monaco-editor vs. @monaco-editor/react <a name="react-monaco-editor-vs-@monaco-editor/react">

The [react-monaco-editor](https://github.com/react-monaco-editor/react-monaco-editor) package was the first package available for embedding with React and is still actively maintained. However, it requires you to make various [webpack](https://blog.logrocket.com/tag/webpack/) changes which are very inconvenient, especially when working with React. This has made the package decline in popularity, as shown by the number of weekly downloads compared to the @monaco-editor/react package. 

The @monaco-editor/react package is embeddable with React and does not require any configurations to start using it. Here’s a chart comparing the react-monaco-editor and @monaco-editor/react packages: ![Comparing React Monaco Editor Packages](http://blog.logrocket.com/wp-content/uploads/2023/04/comparing-monaco-editor-packages-react.png)

## Creating a new app and installing dependencies <a name="creating-new-app-installing-dependencies">

The first step is to create a new React app by running the command below:

```javascript
yarn create react-app monaco-editor-with-react
```

Next, add the react-monaco-editor package with the following command:

```javascript
yarn add react-monaco-editor
```

We also need to install some dependencies to work with the react-monaco-editor. Install them by running the command below:

```javascript
yarn add monaco-editor monaco-editor-core monaco-languageclient
```

We will also install the Monaco webpack plugin as a `devDependency` into our React application with the following command:

```javascript
yarn add --dev monaco-editor-webpack-plugin
```

### Rewiring the React app to work with Monaco Editor <a name="rewiring-react-app-work-with-monaco-editor">

The next step after downloading the dependencies is to make the various webpack changes to allow the Monaco editor to work with our React application. However, it is quite challenging to make webpack changes because webpack configurations are not available to users in [Create React App](https://blog.logrocket.com/getting-started-with-create-react-app/) unless they choose to eject the React app by running `yarn eject`. 

Ejecting a React app is a bad idea because our application will lose all the React configurations and will not benefit from the CRA updates. Some solutions for ejecting our application include using packages like [react-app-rewired](https://github.com/timarney/react-app-rewired) or [rewire](https://github.com/jhnns/rewire). You can also use [CRACO](https://craco.js.org/) to eject your React application, but it needs you to install additional plugins. 

In this tutorial, we will use the react-app-rewired to rewire our React application. Install the package by running the command below:

```javascript
yarn add --dev react-app-rewired
```

Next, create a `config-overrides.js` file in the root of the project, and paste the following code into it:

```javascript
// config-overrides.js

const path = require('path');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');

module.exports = function override(config, env) {
  config.plugins.push(new MonacoWebpackPlugin())
  return config;
}
```

Now, navigate to the `App.js` file, clear all the boilerplate code, and build the code editor.

## Building the code editor in React <a name="building-code-editor-react">

To get started building the code editor in React, paste the code below into the `App.js` file:

```javascript
import MonacoEditor from 'react-monaco-editor';

function App() {
  return (
      <MonacoEditor
        height="400"
      />
  );
}

export default App;
```

Let's go over what it does. In the code above, we imported the `MonacoEditor` from `react-monaco-editor`. Next, we embedded that web editor into the application and passed in the `height` that we wanted the editor to be rendered.

> Note, we didn't pass any language to the editor. The `MonacoEditor` uses JavaScript as the default language when no language is specified. This means you can get all of JavaScript's syntax highlighting and intelligence capabilities.

Now, run `yarn start` to see the changes that you have made in the browser, which should look like the image below: ![The Monaco Editor in React](http://blog.logrocket.com/wp-content/uploads/2023/04/monaco-editor.png)

### Passing `options` to the editor <a name="passing-options-editor">

You can provide a list of `options` to the monaco-editor. The options range from setting the `font size` and `font family` to choosing the `cursor style` and choosing if we want to put the `minimap` in the editor. Now, copy and paste the list of `options` into the `App.js` file:

```javascript
  const options = {
    autoIndent: 'full',
    contextmenu: true,
    fontFamily: 'monospace',
    fontSize: 13,
    lineHeight: 24,
    hideCursorInOverviewRuler: true,
    matchBrackets: 'always',
    minimap: {
      enabled: true,
    },
    scrollbar: {
      horizontalSliderSize: 4,
      verticalSliderSize: 18,
    },
    selectOnLineNumbers: true,
    roundedSelection: false,
    readOnly: false,
    cursorStyle: 'line',
    automaticLayout: true,
  }; 
```

In the code block above, we defined some `options` to customize the appearance and behavior of the editor. The next step is to pass the `options` to the editor, as shown below:

```javascript
<MonacoEditor
    height="400"
    options={options}
/>
```

### Opening files in the editor <a name="opening-files-editor">

So far, we have hardcoded the language of the editor by setting it to JavaScript, and we can see that the code editor works. What happens when we try to write code that doesn't have JavaScript syntax? Take a look at the image below to find out: ![Opening Files in the React Monaco Editor Part One](http://blog.logrocket.com/wp-content/uploads/2023/04/files-monaco-editor.png) In the image above, we wrote some [Python](https://blog.logrocket.com/python-developers-guide-react/) code. You can see that the editor doesn't recognize the syntax, and that is the cause of the errors and squiggly lines. A solution to this is to upload files from our local machine, check the language of each file, and dynamically pass the language to the editor. Let's do that in the code block below:

```javascript
import React, { useState, useEffect } from 'react';
import MonacoEditorfrom 'react-monaco-editor';

const CodeEditor = () => {
  const [code, setCode] = useState('');
  const [file, setFile] = useState();
  const [language, setLanguage] = useState('javascript');

  const handleFileChange = (event) => {
    if (event.target.files) {
      setFile(event.target.files[0]);
    }
  };

  useEffect(() => {
    if (file) {
      var reader = new FileReader();
      reader.onload = async (e) => {
        setCode(e.target.result);
      };
      reader.readAsText(file);
      let newLanguage = 'javascript';
      const extension = file.name.split('.').pop();
      if (['css', 'html', 'python', 'dart'].includes(extension)) {
        newLanguage = extension;
      }
      setLanguage(newLanguage);
    }
  }, [file]);

// The Options object goes here and is passed to the editor below

  return (
    <div>
      <div>
        <input type="file" onChange={handleFileChange} /> 
      </div>
      <hr />
      <MonacoEditor
        height="800"
        language={language}
        value={code}
        options={options}
      />
    </div>
  );
};

const App = () => (
  <div>
    <h2>Monaco Editor </h2>
    <CodeEditor />
  </div>
);
export default App;
```

In the code block above, we created two [`useState` Hooks](https://blog.logrocket.com/guide-usestate-react/) — one for getting and setting the language of the editor and the other for handling the file state. Next, we made a [`useEffect` Hook](https://blog.logrocket.com/useeffect-hook-complete-guide/) to check if there is a file. If there is a file, we get the extension and use it to set the language of the editor to the language of the file that was loaded. 

The next step is creating an `input` button to get files from our machine. Finally, we created a function for handling the file changes and passed it to the `onChange` handler on the `input` button. When we save the changes and navigate to the browser, all the errors and squiggly lines disappear because we are now dynamically setting the language of the editor. Here's what that looks like: ![Opening Files in the React Monaco Editor Part Two](http://blog.logrocket.com/wp-content/uploads/2023/04/files-monaco-editor-two.png)

### Adding `themes` to the code editor <a name="adding-themes-code-editor">

Monaco Editor provides themes for use in the editor. Some of these themes include a `light theme` (the default), a `dark theme`, and a `high-contrast theme`. Let's see how to include the themes in the editor:

```javascript
// The remaining state hooks here remain the same
const [theme, setTheme] = useState('vs-light');

// The fileChange function and useEffect remains the same

const setDarkTheme = (e) => {
    e.preventDefault();
    setTheme((prev) => (prev === 'vs-dark' ? 'hc-black' : 'vs-dark'));
  };

  const setLightTheme = (e) => {
    e.preventDefault();
    setTheme('vs-light');
  };
```

In the code above, we are creating two functions: one for setting the `light theme` and the other for setting the `dark theme`. In the `dark theme` function, we get the previous state of the theme and then set it to the other accordingly. The next step is to pass the functions to the `buttons` that will be used to change the theme. Use the code below:

```javascript
  return (
    <div>
      <div>
        <button onClick={setDarkTheme} type="button">
          Set dark theme ({theme === 'vs-dark' ? 'hc-black' : 'vs-dark'})
        </button>
        {theme !== 'vs-light' && (
          <button onClick={setLightTheme} type="button">
            Set light theme
          </button>
        )}
        <input type="file" onChange={handleFileChange} />
      </div>
      <hr />
      <MonacoEditor
        height="500"
        language={language}
        value={code}
        options={options}
        theme={theme}
      />
    </div>
  );
};
```

In the code block above, we set up the `buttons` that would be used to change the theme of the code editor and then passed the `theme` to the editor. The results should be as shown in the GIF below: ![Themes Using the React Monaco Editor](http://blog.logrocket.com/wp-content/uploads/2023/04/themes-react-monaco-editor.gif)

### Creating a `Diff` editor with the Monaco Editor <a name="creating-diff-editor-with-monaco-editor">

A `Diff` editor is used to compare two files. Fortunately, the Monaco Editor offers functionality for creating a `Diff` editor. Let's dive into some code and see how to create a `Diff` editor in the code block below:

```javascript
import { MonacoDiffEditor } from 'react-monaco-editor';

const DiffEditor = () => {
  const originalCode = 'const firstCode = "Hello from the Monaco Diff Editor!"';
  const modifiedCode =
    'const secondCode = "Lets change this and see what happens next"';

// The Options objects still remains the same 

  return (
    <div>
      <MonacoDiffEditor
        width="800"
        height="300"
        options={options}
        language="javascript"
        original={originalCode}
        value={modifiedCode}
      />
    </div>
  );
};

const App = () => (
  <div className="editor">
    <h2>Another editor (showing a diff)</h2>
    <DiffEditor />
  </div>
);
export default App;
```

In the code block above, we imported the `MonacoDiffEditor` from the react-monaco-editor package. We then created two lines of code to compare and passed them to the `MonacoDiffEditor` as props for the `value` property and the `original` property. The `original` property holds the original value and the `value` property holds the compared code. The result is shown below: ![Final Product Using the React Monaco Editor](http://blog.logrocket.com/wp-content/uploads/2023/04/final-product-react-monaco-editor.png)

## Conclusion

This article has looked at creating a web editor with React and the react-monaco-editor package. Using React and the react-monaco-editor package can greatly simplify the process of creating a web-based code editor. The react-monaco-editor provides a powerful and efficient editor component with [syntax highlighting](https://blog.logrocket.com/guide-syntax-highlighting-react/), autocompletion, and a range of other useful features.

---

##[LogRocket](https://lp.logrocket.com/blg/react-signup-general): Full visibility into your production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket](https://lp.logrocket.com/blg/react-signup-general).

[![LogRocket Signup](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)](https://lp.logrocket.com/blg/react-signup-general)

[LogRocket](https://lp.logrocket.com/blg/react-signup-general) combines session replay, product analytics, and error tracking – empowering software teams to create the ideal web and mobile product experience. What does that mean for you?

Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay problems as if they happened in your own browser to quickly understand what went wrong.

No more noisy alerting. Smart error tracking lets you triage and categorize issues, then learns from this. Get notified of impactful user issues, not false positives. Less alerts, way more useful signal.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — [start monitoring for free](https://lp.logrocket.com/blg/react-signup-general).