How To Embed VSCode Into A Browser With React

Matthew Caseres
Nov 11 · 5 min read
Image for post
Image for post
https://github.com/Open-EdTech/react-run-code

Why I wanted a runnable VSCode clone

To explain why TypeScript is important, you need to explain why developer tooling is important. To explain why developer tooling is important you will need a code editing environment. Fortunately the editor for VSCode (monaco editor) is open source, and feels just like VSCode. By embedding the monaco editor in my web page I can explain TypeScript much better than any other website.

Plus, the monaco editor can transpile TypeScript to JavaScript, we can run the JavaScript in the browser and output the results for an interactive educational experience.

Running Code

By default, the monaco editor does not run code. It does syntax highlighting, auto-completion, red squiggly lines, hover information, etc. So how are we running code?

Basically we do this:

Running TypeScript

I’m pretty sure can’t just run TypeScript code like that, we need to run it as JavaScript. The monaco editor has a TypeScript compiler that it uses to check your TypeScript code. You can use that to emit the output from a single model, a model is basically just a file in VSCode.

Then you take the emitted JavaScript and run it.

Building a Console

The console-feed react component was such a time saver (and it looks awesome!)

Image for post
Image for post

This component reads from the console’s messages and outputs the formatting shown in the gif. We modified this component to clear the logs on each run to prevent logs from piling up endlessly.

Supporting Multiple Consoles

We want multiple editors per page, by default their consoles would all print the same message because we are just reading logs from the console. How do we isolate console messages by the editor that sent the message?

We make each editor output their unique editor ID as the last argument in an overridden console.log to distinguish between message sources.

We only log the message to a console component if the last argument provided matched the ID of the console component.

Grading User Input

I had an idea that it might be useful to “grade” user code in a way that doesn’t involve any calls to any servers. The issue is that you will have a tough time calling an API from code that is running inside of Function(code)() . Also, since the grading process happens entirely on the client side there is no way to make the grading “unhackable”.

So we do something simple. If the message from the console is “Problem solved”, the problem is solved. By solved I mean that the owner of the website can use a callback function that executes custom logic when this message is logged.

Image for post
Image for post
console.log(“Problem solved”) executes user defined function.

Supporting Multiple Files

Tabs

Tabs do not come with monaco editor. I styled the tabs and implemented tab creation and deletion.

Image for post
Image for post

I also used React-Dnd to rearrange tabs the same way you would in VSCode.

Image for post
Image for post

Import and Export

I took a very hacky approach to module bundling (there are known bugs). I generate a dependency graph using regular expressions.

Then perform a topological sort to stack the files on top of each other. I get to use some really nice (and well tested!) code from this LeetCode problem’s discussion section.

The models are shared across the page so I can import from different editors on the same page.

Image for post
Image for post

The hacky way of doing things doesn’t always work. If you open up a file named “0.ts” it will show you the code that got generated so you can diagnose the problem. Here we got screwed up by a duplicate declaration.

Image for post
Image for post

Customizing Files

I included some different options for the files, you can customize to determine which tab is initially selected, if the file should be read only, if the file should be shown, and more.

Quickly Writing Interactive Content

To create the initial state for the editor you can create an empty editor, make a new file, and copy the configuration for modelsInfo to the clipboard using the green <> .

import React from "react";
import Editor from "react-run-code";

function App() {
return <Editor id="10" modelsInfo={[]} />;
}

export default App;
Image for post
Image for post

You can now go into your source code and paste [{"value":"console.log(\"make a new file\")","filename":"new.ts","language":"typescript"}] in place of [] for the prop modelsInfo={[]}.

Next Steps

I have tested the component a lot manually, but I need to learn Jest and get some of those type of tests up and running! I still am debating if I should try to implement runtime bundling, I saw a good blog post on the subject.

Summary

The distance between just another blog and something that can be really special (an educational platform?) is just the time you put into building internal tools to quickly create content rich interactive components. The component isn’t perfect but it has worked well enough for me to start using it.

Try it out on npm.

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store