19
Collaborative Coding in Monaco Editor
Code-Pair/Collaborative Coding is a trending topic in the landscape of remote-working/remote-hiring. In this post, We will discuss how we can build such a system on our own in less than 20 minutes.
![](https://res.cloudinary.com/practicaldev/image/fetch/s--iNaTn5vI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/errrdu0lkb0bkaqu5nm7.png)
In Code-Pairing there are more than one users sharing the same code and editing it in real-time. Code-Pairing has lots of use-cases like helping a teammate, instructing interns/students, real time interviews etc. Possibilities are endless.
Today we will talk about how we can build such a system, using some of the most popular tools in the current tech world. We are going to build this project in ReactJs but keep in mind end product can be achieved through any framework.
We will be using Monaco-Editor as our code editor. If you don't already know, Monaco-Editor is the same Editor which powers VSCode. Developed by Microsoft, is currently one of the most powerful opensource editor in market.
We will also be using Firebase's Realtime Database as backend.
We will be using an amazing library @hackerrank/firepad, This library will automatically take care of managing editor-state in Firebase automatically for us. Not only that but it also automatically highlight the cursor of each user and where exactly other users are typing.
Lets get started with the setup.
npx create-react-app collaboratory
yarn add @hackerrank/firepad [email protected] @monaco-editor/react monaco-editor
Lets add editor by modifying our App.js and lets start the developement server by npm start
.
import Editor from "@monaco-editor/react";
import {useRef,useState} from 'react';
function App() {
const editorRef = useRef(null);
const [editorLoaded,setEditorLoaded] = useState(false);
function handleEditorDidMount(editor, monaco) {
editorRef.current = editor;
setEditorLoaded(true);
}
return (
<div>
<Editor
height="90vh"
defaultLanguage="javascript"
theme="vs-dark"
defaultValue="// Welcome to My Editor"
onMount={handleEditorDidMount}
/>
</div>
);
}
export default App;
If you already have firebase set up skip to next step.
Go to https://console.firebase.google.com/u/2/.
![](https://res.cloudinary.com/practicaldev/image/fetch/s--5Dy_NJF1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hwgu2ey97e71d5jqf2tk.jpg)
![](https://res.cloudinary.com/practicaldev/image/fetch/s--RhIxAdtF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufy9j5f52wo1gsn0u3uc.jpg)
![](https://res.cloudinary.com/practicaldev/image/fetch/s--TFqKxMFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s08wc9e8lo56cybe7d9l.jpg)
![](https://res.cloudinary.com/practicaldev/image/fetch/s--dr3-NV8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fy9ivr9g6xmgz4y67w90.jpg)
![](https://res.cloudinary.com/practicaldev/image/fetch/s--EuvnmjLX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aiux6waykp6hg37iuh7v.jpg)
Create firebaseConfig.js
file in src
folder.
const firebaseConfig = {
apiKey: "#####################################", // important
authDomain: "############.firebaseapp.com", // important
databaseURL: "https://########.firebaseio.com", // important
projectId: "###########",
storageBucket: "#########.appspot.com",
messagingSenderId: "############3",
appId: "#############",
measurementId: "G-########"
};
export default firebaseConfig;
Import firebase sdk
import firebase from "firebase";
import firebaseConfig from './firebaseConfig';
Inside App.js, We do a initialization of firebase sdk
useEffect(() => {
if(!firebase.apps.length){
// Make sure initialization happens only once
firebase.initializeApp(firebaseConfig);
}
else{
firebase.app();
}
}, []);
Import Firepad Monaco Adapter
import {fromMonaco} from '@hackerrank/firepad';
We initialize firepad adapter for monaco
useEffect(() => {
if(!editorLoaded){
// If editor is not loaded return
return;
}
const dbRef = firebase.database().ref().child(`pair001`); // Can be anything in param, use unique string for unique code session
const firepad = fromMonaco(dbRef,editorRef.current);
const name = prompt("Enter your Name :"); // Name to highlight who is editing where in the code
if(name){
firepad.setUserName(name);
}
},[editorLoaded]);
That's It. We should be good to go. Open project localhost:3000 in two separate tabs/window and start typing in one. Other should update as well.
Final Code should look like the following.
import React {useRef,useEffect,useState} from 'react';
import Editor from "@monaco-editor/react";
import firebaseConfig from './firebaseConfig';
import firebase from "firebase";
import {fromMonaco} from '@hackerrank/firepad';
function App() {
const editorRef = useRef(null);
const [editorLoaded, setEditorLoaded] = useState(false);
function handleEditorDidMount(editor, monaco) {
editorRef.current = editor;
setEditorLoaded(true);
}
useEffect(() => {
if(!firebase.app.length){
firebase.initializeApp(firebaseConfig);
}
else{
firebase.app();
}
}, []);
useEffect(() => {
if(!editorLoaded){
return;
}
const dbRef = firebase.database().ref().child(`pair001`);
const firepad = fromMonaco(dbRef,editorRef.current);
const name = prompt("Enter your Name :");
firepad.setUserName(name);
},[editorLoaded]);
return (
<div>
<Editor
height="90vh"
defaultLanguage="javascript"
theme="vs-dark"
defaultValue="// Welcome to My Editor"
onMount={handleEditorDidMount}
/>
</div>
);
}
export default App;
My name is Shubham Shekhar, I am a Software Developer, I have experience in ReactJs, Redux, NextJs, Material Ui, Bootstrap, Testing-Library, Jest, NodeJs, etc. I am highly focused towards learning new things and improving my way up. Feel free to contact me.
Github : https://github.com/Shubham567
Contact Me: [email protected]
19