An online web code editor is most useful when you do not have the opportunity to use a code editor application, or when you want to quickly try out something on the web with your computer or even your mobile phone. This is also an interesting project to work on because having the knowledge of how to build a code editor will give you ideas on how to approach other projects that require you to integrate a code editor to show some functionality.
Here are a few React concepts you’ll need to know in order to follow along in this article:
- Component structure,
- Functional components,
A rich programming API and a CSS theming system are available for customizing CodeMirror to fit your application and extending it with new functionality. It gives us the functionality to create a rich code editor that runs on the web and shows us the result of our code in real time.
In the next section, we will set up our new React project and install the libraries we need to build our web app.
Creating A New React Project
Let’s start by creating a new React project. In your commandline interface, navigate to the directory in which you want to create your project, and let’s create a React application and name it
npx create-react-app code_editor
Having created our new React application, let’s navigate to that project’s directory in the commandline interface:
There are two libraries we need to install here:
npm install codemirror react-codemirror2
Instead of creating individual buttons, let’s make the button a component that is reusable. In our project, the button would have three instances, according to the three tabs we need.
Create a folder named
components in the
src folder. In this new
components folder, create a JSX file named
Here is all of the code needed in the
import React from 'react' const Button = (title, onClick) => return () export default Button
Here is a full explanation of what we did above:
- We created a functional component named
Button, which we then exported.
- We destructured
onClickfrom the props coming into the component. Here,
titlewould be a string of text, and
onClickwould be a function that gets called when a button is clicked.
- Next, we used the
buttonelement to declare our button, and used the
styleattributes to style our button to look presentable.
- We added the
onClickattribute and passed our destructured
onClickfunction props to it.
- The last thing you’ll notice we did in this component is pass in
titleas the content of the
buttontag. This allows us to display the title dynamically, based on what prop is being passed to the instance of the button component when it is called.
Now that we have created a reusable button component, let’s move on and bring our component into
App.js. Go to
App.js and import the newly created button component:
import Button from './components/Button';
To track which tab or editor is open, we need a declare state to hold the value of the editor that is open. Using the
useState React hook, we’ll set up the state that will store the name of the editor tab that is currently open when that tab’s button is clicked.
Here is how we do that:
import React, useState from 'react'; import './App.css'; import Button from './components/Button'; function App() const [openedEditor, setOpenedEditor] = useState('html'); return (); export default App;
Here, we declared our state. It takes the name of the editor that is currently open. Because the value
html is passed as the state’s default value, the HTML editor would be the tab open by default.
Let’s move on and write the function that will use
setOpenedEditor to change the value of the state when a tab button is clicked.
Note: Two tabs may not be open at the same time, so we’ll have to consider that when writing our function.
Here is what our function, named
onTabClick, looks like:
import React, useState from 'react'; import './App.css'; import Button from './components/Button'; function App() ... const onTabClick = (editorName) => setOpenedEditor(editorName); ; return (); export default App;
Here, we passed a single function argument, which is the name of the tab currently selected. This argument would be supplied anywhere the function is called, and the relevant name of that tab would be passed in.
Let’s create three instances of our
Button for the three tabs we need:
Welcome to the editor!
Here is what we did:
- We started by adding a
ptag, basically just to give some context to what our application is about.
- We used a
divtag to wrap our tab buttons. The
divtag carries a
classNamethat we will use to style the buttons into a grid display in the CSS file later in this tutorial.
- Next, we declared three instances of the
Buttoncomponent. If you recall, the
Buttoncomponent takes two props,
onClick. In every instance of the
Buttoncomponent, these two props are provided.
titleprop takes the title of the tab.
onClickprop takes a function,
onTabClick, which we just created and which takes a single argument: the name of the tab selected.
openedEditor state is set to
setOpenedEditor('html')), then the tab for the HTML section would become the currently visible tab. You’ll understand this better as we do it below:
... return (...); ...openedEditor === 'html' ? (
The html editor is open) : openedEditor === 'css' ? (
The CSS editor is open!!!!!!) : (
Let’s go over the code above in plain English. If the value of
html, then display the HTML section. Otherwise, if the value of
css, then display the CSS section. Otherwise, if the value is neither
css, then that means the value must be
js, because we have only three possible values for the
We used paragraph tags (
p) for the different sections in the ternary operator conditions. As we proceed, we will create the editor components and replace the
p tags with the editor components themselves.
We have come so far already! When a button is clicked, it fires up the action that sets the tab it represents to
true, making that tab visible. Here’s what our app currently looks like:
Let’s add a little CSS to the
div container holding the buttons. We want the buttons to be displayed in a grid, instead of stacked vertically like in the image above. Go to your
App.css file and add the following code:
.tab-button-container display: flex;
Recall that we added
className="tab-button-container" as an attribute in the
div tag holding the three-tab buttons. Here, we styled that container, using CSS to set its display to
flex. This is the result:
Be proud of how much you’ve done to get to this point. In the next section, we will create our editors, replacing the
p tags with them.
Creating the Editors
Because we have already installed the libraries we are going to be working on within our CodeMirror editor, let’s go ahead and create our
Editor.jsx file in the
components > Editor.jsx
Having created our new file, let’s write some initial code in it:
import React, useState from 'react'; import 'codemirror/lib/codemirror.css'; import Controlled as ControlledEditorComponent from 'react-codemirror2'; const Editor = ( language, value, setEditorState ) => return () export default Editor
Here’s what we did:
- We imported React alongside the
useStatehook because we are going to need it.
- We imported the CodeMirror CSS file (which comes from the CodeMirror library that we installed, so you don’t have to install it in any special way).
- We imported
react-codemirror2, renaming it to
ControlledEditorComponentto make it clearer. We will be using this shortly.
- Then, we declared our
Editorfunctional component, and we have a return statement with an empty
div, with a
classNamein the return statement for now.
In our functional component, we destructured some values from the props, including
setEditorState. These three props would be supplied in any instance of the editor when it is called in
ControlledEditorComponent to write the code for our editor. Here’s what we’ll do:
Let’s walk through what we did here, explaining some CodeMirror terms.
The CodeMirror modes specify which language an editor is meant for. We imported three modes because we have three editors for this project:
- XML: This mode is for HTML. It uses the term XML.
- CSS: This (
codemirror/mode/css/css) brings in CSS mode.
Note: Because the editor is built as a component that is reusable, we cannot put a direct mode in the editor. So, we supply the mode through the
language prop that we destructured. But this doesn’t change the fact that the modes need to be imported in order to work.
Next, let’s discuss the things in
This is called anytime you write to or remove from the editor. Think of this like the
onChangehandler you would normally have in an input field to track changes. Using this, we will be able to get the value of our editor anytime there’s a new change and save it to our editor’s state. We will write the
handleChangefunction as we proceed.
value = value
This is just the content of the editor at any given time. We passed a destructured prop named
valueto this attribute. The
valueprops is the state holding the value of that editor. This would be supplied from the editor’s instance.
This class name is not a style we make ourselves. It is supplied from CodeMirror’s CSS file, which we imported above.
This is an object that takes the different functionality we want our editor to have. There are many amazing options in CodeMirror. Let’s look at the ones we used here:
This means that code should wrap to the next line when the line is full.
This allows linting.
This mode, as discussed above, takes the language that the editor is going to be used for. The language has already been imported above, but the editor is going to apply a language based on the
languagevalue supplied to the editor via the prop.
This specifies that the editor should have line numbers for each line.
Next, we can write the
handleChange function for the
const handleChange = (editor, data, value) => setEditorState(value);
onBeforeChange handler gives us access to three things:
editor, data, value.
We only need the
value because it is what we want to pass in our
setEditorState prop. The
setEditorState prop represents the set value for each state that we declared in
App.js, holding the value for each editor. As we move on, we will look at how to pass this as a prop to the
Next, we’ll add a dropdown that allows us to select different themes for the editor. So, let’s look at themes in CodeMirror.
CodeMirror has multiple themes we can select from. Visit the official website to see demos of the different themes available. Let’s make a dropdown with different themes that the user can choose from in our editor. For this tutorial, we’ll be adding five themes, but you can add as many as you like.
First, let’s import our themes in the
import 'codemirror/theme/dracula.css'; import 'codemirror/theme/material.css'; import 'codemirror/theme/mdn-like.css'; import 'codemirror/theme/the-matrix.css'; import 'codemirror/theme/night.css';
Next, create an array of all of the themes we have imported:
const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']
Let’s declare a
useState hook to hold the value of the selected theme, and set the default theme as
const [theme, setTheme] = useState("dracula")
Let’s create the dropdown:
... return () ...// the rest of the code comes below...
In the code above, we used the
label HTML tag to add a label to our dropdown, and then added the
select HTML tag to create our dropdown. The
option tag in the
select element defines the options available in the dropdown.
Because we needed to fill the dropdown with the theme names in the
themeArray that we created, we used the
.map array method to map
themeArray and display the names individually using the
Hold on — we’re not done explaining the code above. In the opening
select tag, we passed the
onChange attribute to track and update the
theme state whenever a new value is selected in the dropdown. Whenever a new option is selected in the dropdown, the value is gotten from the object returned to us. Next, we use the
setTheme from our state hook to set the new value to be the value that the state holds.
At this point, we have created our dropdown, set up our theme’s state, and written our function to set the state with the new value. The final thing we need to do to make CodeMirror use our theme is pass the theme to the
options object in
ControlledEditorComponent. In the
options object, let’s add a value named
theme, and set its value to the state’s value for the selected theme, also named
ControlledEditorComponent would look like now:
Now, we have made a dropdown of different themes that can be selected from in the editor.
Here’s what the full code in
Editor.js looks like at the moment:
There’s only one
className that we need to style. Go to
App.css and add the following style:
.editor-container padding-top: 0.4%;
Now that our editors are ready, let’s go back to
App.js and use them there.
src > App.js
The first thing we need to do is import the
Editor.js component in here:
import Editor from './components/Editor';
const [html, setHtml] = useState(''); const [css, setCss] = useState(''); const [js, setJs] = useState('');
If you recall, we will need to use these states to hold and supply the contents of our editors.
Next, let’s replace the paragraph (
function App() ... return (); export default App;
Welcome to the edior// This is where the tab buttons container is...htmlEditorIsOpen ? (
) : cssEditorIsOpen ? ( ) : ( )
If you’ve been following along until now, you’ll understand what we did in the code block above.
Here it is in plain English: We replaced the
p tags (which were there as placeholders) with instances of the editor components. Then, we supplied their
setEditorState props, respectively, to match their corresponding states.
We’ve come so far! Here is what our app looks like now:
Introduction to Iframes
We’ll be making use of inline frames (iframes) to display the result of the code entered in the editor.
According to MDN:
The HTML Inline Frame element (
) represents a nested browsing context, embedding another HTML page into the current one.
How Iframes Work in React
Iframes are normally used with plain HTML. Using Iframes with React doesn’t require many changes, the major one being to convert attribute names to camelcase. An example of this is that
srcdoc would become
The Future of Iframes on the Web
Iframes continue to be really useful in web development. Something you might want to check out is Portals. As Daniel Brain explains:
“Portals introduce a powerful new set of capabilities into this mix. Now it’s possible to build something that feels like an iframe, that can seamlessly animate and morph and take over the full browser window.”
One of the things Portals tries to solve is the URL bar problem. When using iframe, components rendered in the iframe don’t carry a unique URL in the address bar; as such, this might not be great for the user experience, depending on the use case. Portals is worth checking out, and I’d suggest you do that, but because it is not the focus of our article, this is all I’ll say about it here.
Creating the Iframe to House Our Result
Let’s move ahead with our tutorial by creating an iframe to house the result of our editors.
return (// ...);
Here, we created the iframe and housed it in a
div container tag. In the iframe, we passed some attributes that we need:
srcDocattribute is written in camelcase because this is how to write iframe attributes in React. When using an iframe, we can either embed an external web page on the page or render specified HTML content. To load and embed an external page, we would use the
srcproperty instead. In our case, we are not loading an external page; rather, we want to create a new internal HTML document that houses our result; for this, we need the
srcDocattribute. This attribute takes the HTML document that we want to embed (we have not created that yet, but we will soon).
The title attribute is used to describe the contents of the inline frame.
This property has many purposes. In our case, we are using it to allow scripts to run in our iframe with the
This merely defines the border thickness of the iframe.
This defines the width and height of the iframe.
These terms should now make more sense to you. Let’s move on and declare the state that will hold the HTML template document for
srcDoc. If you look closely at the code block above, you’ll see that we passed a value to the
=srcDoc. Let’s use our
useState() React hook to declare the
srcDoc state. To do this, in the
App.js file, go to where we defined the other states and add this one:
const [srcDoc, setSrcDoc] = useState(` `);
Now that we have created the state, the next thing to do is display the result in the state whenever we type in the code editor. But what we don’t want is to re-render the component on every single key press. With that in mind, let’s proceed.
Configuring the Iframe to Display the Result
useEffect() to be triggered, and that will render the updated result in the iframe. Let’s write
useEffect() to do this in the
First, import the
import React, useState, useEffect from 'react';
useEffect() like so:
useEffect(() => const timeOut = setTimeout(() => setSrcDoc( ` $html ` ) , 250); return () => clearTimeout(timeOut) , [html, css, js])
Here, we wrote a
Why did we need to use
setTimeout()? Well, if we wrote this without it, then every time a single key press is made in an editor, our iframe would be updated, and that isn’t great for performance generally. So we use
setTimeout() to delay the update for 250 milliseconds, giving us enough time to know whether the user is still typing. That is, every time the user presses a key, it restarts the count, so the iframe would only be updated when the user has been idle (not typing) for 250 milliseconds. This is a cool way to avoid having to update the iframe every time a key is pressed.
The next thing we did above was to update
srcDoc with the new changes. The
srcDoc component, as we explained above, renders specified HTML content in the iframe. In our code, we passed an HTML template, taking the
html state that contains the code that the user has typed into the HTML editor and placing it between the
body tags of our template. We also took the
css state that contains the styles that the user has typed in the CSS editor, and we passed that between the
style tags. Finally, we took the
Notice that in setting
setSrcDoc, we used backticks (
` `) instead of normal quotes (
' '). This is because backticks allow us to pass in corresponding state values, as we did in the code above.
return statement in the
useEffect() hook is a cleanup function that clears
setTimeout() when it is complete, to avoid memory leakage. The documentation has more about
Here’s what our project looks like at the moment:
With CodeMirror addons, we can enhance our editor with more of the kind of functionality we would find in other code editors. Let’s walk through an example of closing tags being added automatically when an opening tag is typed, and another example of a bracket automatically closing when the opening bracket is inputted:
The first thing to do is import the addon for this into our
import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/edit/closebrackets';
Let’s pass it in the
Now here’s what we have:
You could add a ton of these addons to your editor to give it richer features. We couldn’t possibly go through all of them here.
Now that we are done with this, let’s briefly discuss things we could do to improve our app’s accessibility and performance.
Performance and Accessibility of the Solution
Looking at our web code editor, some things could definitely be improved upon.
Because we’ve paid attention primarily to functionality, we might have neglected design a little bit. For better accessibility, here are some things you could do to improve this solution:
- You could set an
activeclass on the button for the currently open editor. Highlighting the button would improve accessibility by giving users a clear indication of which editor they’re currently working on.
- You might want the editor to occupy more screen space than what we have here. Another thing you could try is making the iframe pop up with the click of a button that is docked somewhere to the side. Doing so would give the editor more screen space.
- This sort of editor would be useful for people who want to run a quick exercise on their mobile device, so fully adapting it to mobile would be necessary (not to mention both of the points about mobile above).
- Currently, we are able to switch the theme of the editor component from among the multiple themes we’ve loaded in, but the general theme of the page remains the same. You could enable the user to switch between a dark and light theme for the entire layout. This would be good for accessibility, relieving the strain on people’s eyes from looking at a bright screen for too long.
- We didn’t look at security issues with our iframe, mainly because we were loading an internal HTML document in the iframe, rather than an external document. So we don’t need to consider this too carefully because iframes are a good fit for our use case.
- With iframes, another consideration would be page-loading time, because the content being loaded in the iframe would normally be out of your control. In our app, this isn’t an issue because our iframe content isn’t external.
Performance and accessibility are worth a lot of consideration when you’re building any application because they will determine how useful and usable your application is to its users.
Shedrack has done a good job of explaining methods for improving and optimizing performance in React apps. It’s worth checking out!
Working through different projects helps us to learn about a wide range of subjects. Now that you’ve gone through this article, feel free to expand upon your experience by experimenting with more add-ons to make the code editor richer, revamping the UI, and fixing the accessibility and performance concerns outlined above.
Here’s the demo on Codesandbox:
Links and Material
(ks, vf, yk, il, al)