Building a Live Code Sharing Platform With Dyte and React
TL;DR
At the conclusion of this tutorial, we will have created a “Live Code Sharing Platform” that allows users to share code and engage in video and audio calls. 🎉💻🥳
Introduction
Code sharing is an essential aspect of programming. With the rise of remote work and virtual collaboration, developers need reliable tools for code sharing that offer real-time communication, video and audio conferencing, and a friendly user interface.
Codeshare.io is one such example. But today, we're going to roll up our sleeves and build our very own code sharing playground using Dyte.io.
Buckle up! 🎢
Dyte is a developer-friendly platform that offers powerful SDKs to build live experiences within our product.
In this blog, we will walk you through the process of building a code sharing platform with Dyte.io and ReactJs. Let’s start! 🏃
Step 0: Setting up Dyte account
Before anything, we would need to setup a Dyte account. For this, first visit Dyte.io and then hit Start Building. On the next page, Sign in with Google or GitHub account to get your free Dyte account 🎉. You will find your API keys under the API Keys tab on the left sidebar. Keep your API keys secure and don’t share them with anyone.
Step 1: Setting up the environment
Hitting one more checkpoint before we dive into coding.
We will be using Node.js, a popular JavaScript runtime environment, and create-react-app, a tool that generates a React project with a pre-configured setup.
To get started, we will create three folders client
, server
, and plugin
.
Note: 🧑💻 If you are on Mac, you should turn off “AirPlay Receiver” in System Settings as it occupied Port 5000 by default.
Just for reference, this is how our final folder structure
would look like at the end of this blog.
We will go ahead and install Dyte
CLI using the command below.
$ npm install -g @dytesdk/cli
Going ahead with the authorization part and selecting the organization with the following commands.
$ dyte auth login $ dyte auth org
For more information, visit Dyte CLI Docs.
Step 2: Setting up a new Custom Plugin
To start building a custom Dyte Plugin we will clone Dyte Plugin Template
using the following command. The plugin template allows us to get started quicker.
$ git clone https://github.com/dyte-in/react-plugin-template.git
This template uses @dytesdk/plugin-sdk
and allows us to create our own real-time plugins that work seamlessly with Dyte meetings. It has many interesting features to help us solve complex problems in minutes. Now, we will install the dependencies using the “npm install” command.
$ npm install
Next, we will add a couple of dependencies by running the following command.
$ npm i @uiw/react-codemirror @codemirror/lang-javascript uuid
Here, we are adding react-codemirror
, which provides a pre-built Code Editor with language support. We are also installing UUID that will help us in generating UUIDs with just a function call. This will come in handy soon. Now that we have everything set up, we can use this command to start and test our Custom Plugin Setup.
$ npm start
Step 3: Trying out our new Custom Plugin
To try using our new custom plugin, we will have to visit http://staging.dyte.io
Here, we will be prompted to create a new meeting. It is super simple, just add your name and a meeting name and hit Create
. On the next page, it will ask you to join
the meeting. Click on join and you’re in.
Find the Plugins
button in the bottom-right corner, and click on it to reveal all existing plugins. We are interested in a plugin named Localhost Dev
, click on launch
and it will reveal your plugin inside the meeting itself 🤯.
We have everything ready with us. Now, we can get started with writing some actual code!
Let’s begin with our Code Editor component.
Step 4: Creating our Code Editor
Let’s get started with creating our own code editor 🧑💻.
For this, we are going to first create a component and then use the CodeMirror
package that we installed earlier. First, create a new React Functional Component
in file named CodeEditor.js
inside src/containers
and paste the following code.
<CodeMirror
style={{ fontSize: "32px", textAlign: "left" }}
value={code}
onChange={handleCodeChange}
height="100vh"
width="100vw"
theme={"dark"}
extensions={[javascript({ jsx: true })]}
/>;
CodeMirror component provides a pre-built Code Editor. It comes with various syntax highlighting features.
Step 5: Handling Code Changes
To work on handling the live code changes, let's first create a new state named code
import { useEffect, useState, useRef } from "react";
const [code, setCode] = useState("function add(a, b) { return a + b;}");
Now, we will create a handleCodeChange
function that will emit events whenever there is a change in our code in CodeMirror
using plugin.emit()
function.
Here, we are emitting an object, that has two properties. The first one is a randomly generated user id
and the second one is our whole code.
import { useEffect, useState, useRef } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";
const CodeEditor = ({ plugin }) => {
const [code, setCode] = useState("function add(a, b) {return a + b;}");
const [userId, setUserId] = useState();
const handleCodeChange = async (code) => {
plugin.emit(CODE_CHANGE, { code, user });
};
return (
<>
<CodeMirror
style={{ fontSize: "32px", textAlign: "left" }}
value={code}
onChange={handleCodeChange}
height="100vh"
width="100vw"
theme={"dark"}
extensions={[javascript({ jsx: true })]}
/>
</>
);
};
export default CodeEditor;
Step 6: Listening to Code Change Events
We need to listen to the event when other people change the code. For this, we will use the plugin.on()
function as shown below. The function accepts event name
as a parameter and receives the code changes.
One more thing to note here is that we have to update our current code only if it is sent by other users. For this we need to put a simple conditional statement if(data.user != userId){}
import { useEffect, useState, useRef } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";
import { v4 } from "uuid";
const user = v4();
const CodeEditor = ({ plugin }) => {
const [code, setCode] = useState("function add(a, b) {\n return a + b;\n}");
const [userId, setUserId] = useState();
useEffect(() => {
if (plugin) {
const startListening = async () => {
plugin.on(CODE_CHANGE, (data) => {
if (data.user != user) {
setCode(data.code);
}
});
};
startListening();
}
}, [plugin]);
const handleCodeChange = async (code) => {
plugin.emit(CODE_CHANGE, { code, user });
};
return (
<>
{" "}
<CodeMirror
style={{ fontSize: "32px", textAlign: "left" }}
value={code}
onChange={handleCodeChange}
height="100vh"
width="100vw"
theme={"dark"}
extensions={[javascript({ jsx: true })]}
/>{" "}
</>
);
};
export default CodeEditor;
In this component, we are creating a Code Editor using CodeMirror. Any changes to the editor emit an event CODE_CHANGE
to all users in the meeting, using plugin.emit()
function call. emit
function takes eventName
and data
as arguments.
In the next step, we need to import the CodeEditor component to Main.tsx
file. Your file should look something like this. 👇
import { useDytePlugin } from "../context";
import CodeEditor from "./CodeEditor";
const Main = () => {
const plugin = useDytePlugin();
return <div style={{ height: "100%" }} />;
};
export default Main;
Code for our “Collaborative Code Editor Plugin” 😉 is now ready. How did someone write the first code editor without a code editor 😂? Jokes aside, we are ready with our Plugin 🎉.
To checkout, open up staging.dyte.io and follow along. Enter your name and meeting title to get in. Hit join meeting. Open up the Localhost Dev
plugin and you are good to go.
Step 7: Publishing our Component
🧑💻 Now, it’s time to publish our content, this is a simple process with Dyte CLI
. For this we have to first build our plugin and then run dyte plugins publish
command.
$ dyte plugins create
$ npm run build
$ cp dyte-config.json ./build/dyte-config.json
$ cd build $ dyte plugins publish
Step 8: Getting started with our Code Sharing Platform
Now that we have built the plugin which will help us collaborate on code, we can get started with building the platform to use this plugin on.
Let's start with the client side. Inside the client
folder, we will set up a new ReactJS
project using create-react-app
and create our react app using the following command.
$ npx create-react-app .
Next, let us install the dependencies of Dyte
and code-editor
by running the following command:
$ npm i @dytesdk/react-ui-kit @dytesdk/react-web-core react-simple-code-editor
🎬 Now, let’s start our development server with npm start:
$ npm start
Step 9: Building the Layout
Let us open app.js
file inside the src
folder. We will remove the contents of this file and add the following code snippet 👇.
import Layout from "./components/Layout";
function App() {}
export default App;
Next, we will write the Layout
component, we will be creating a layout with our logo, title and meeting UI.
We will use several libraries, including DyteMeeting
and PrismJS, for building a collaborative code editor and meeting UI.
import Meet from "./Meeting"
const Layout = () => {
return (
<>
<div style={{ padding: "30px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<img src="https://dyte.io/blog/content/images/2021/09/Dyte-Logo.svg" height={"70px"}/>
<span style={{ fontSize: "30px", color: "#3e75fd" }}>Collaborative Code Editor</span>
<img style={{ opacity: "0"}} src="https://dyte.io/blog/content/images/2021/09/Dyte-Logo.svg" height={"80px"}/>
</div>
<div style={{ height: "88vh" }} ><Meet /></div>
</>
)
}
export default Layout
Step 10: The Meeting Component
🧑💻 First, we need to create a few utility functions in a file client/src/utils/api.js
const createMeeting = async () => {
const resp = await fetch("http://localhost:3000/meetings", {
method: "POST",
body: JSON.stringify({ title: "New Code pair" }),
headers: { "Content-Type": "application/json" }
})
const data = await resp.json()
console.log(data)
return data.data.id;
}
const joinMeeting = async (id) => {
const resp = await fetch(`http://localhost:3000/meetings/${id}/participants`, {
method: "POST",
body: JSON.stringify({ name: "new user", preset_name: "group_call_host" }),
headers: { "Content-Type": "application/json" }
})
const data = await resp.json()
console.log(data)
return data.data.token;
}
export { createMeeting, joinMeeting }
These functions talk to our backend to create meetings and add participants. For meeting creation, we pass title
as an optional parameter.
And for adding participants, we pass name
parameter (optional), picture
parameter (optional), and preset_name
parameter (required) along with meetingId
.
Time for our Meeting component. For this, we will use Dyte UI kit ✨ which makes it super easy to integrate live Audio/Video sharing in your application. Yes, these 10-15 lines of code do all the heavy lifting 🏋🏼♂️.
import { useState, useEffect, useRef } from "react";
import { DyteMeeting, provideDyteDesignSystem } from "@dytesdk/react-ui-kit";
import { useDyteClient } from "@dytesdk/react-web-core";
import { createMeeting, joinMeeting } from "../utils/api";
const Meet = () => {
const meetingEl = useRef();
const [meeting, initMeeting] = useDyteClient();
const [userToken, setUserToken] = useState();
const [meetingId, setMeetingId] = useState();
const createMeetingId = async () => {
const newMeetingId = await createMeeting();
setMeetingId(newMeetingId);
};
useEffect(() => {
const id = window.location.pathname.split("/")[2];
if (!id) {
createMeetingId();
} else {
setMeetingId(id);
}
}, []);
const joinMeetingId = async () => {
if (meetingId) {
const authToken = await joinMeeting(meetingId);
await initMeeting({
authToken,
modules: {
plugin: true,
devTools: {
logs: true,
plugins: [
{
name: "Collaborative-code-editor",
port: "5000",
id: "<your-plugin-id>",
},
],
},
},
});
setUserToken(authToken);
}
};
useEffect(() => {
if (meetingId && !userToken) joinMeetingId();
}, [meetingId]);
useEffect(() => {
if (userToken) {
provideDyteDesignSystem(meetingEl.current, {
theme: "dark",
});
}
}, [userToken]);
return (
<>
{userToken && meetingId ? (
<DyteMeeting mode="fill" meeting={meeting} ref={meetingEl} />
) : (
<div>Loading...</div>
)}
</>
);
};
export default Meet;
We are ready with our Code Sharing Platform's UI now 🎉
Step 11: Getting the Backend Ready
🧑💻 Dyte provides a variety of powerful APIs that enhance the developer experience and meet a wide range of developer requirements.
We can manage Dyte’s organizations, sessions, meetings, recordings, webhooks, live streaming, analytics, and much more.
To simplify the process, we will use Express with Node to create a backend which will help with authentication, meeting creation, and adding participants. ✨
To get started in the project folder, follow the following steps:
$ mkdir server && cd server
We’ll start with installing a couple of dependencies, cd into the 'server' directory, and use the following command.
$ npm init -y
$ npm install express cors axios dotenv
$ npm install -g nodemon
First, let's create a .env
file to store our API key in server/src
. You can find these keys on Dyte Dashboard.
DYTE_ORG_ID=
DYTE_API_KEY=
Let’s also add a few scripts ✍️ that will help us run our server
application. Add the below lines in your package.json
file inside scripts
tag.
"start": "node dist/index.js",
"dev": "nodemon src/index.js"
Let's create our files and folders now. All our code will live inside server/src
folder. Inside src
create another folder utils
.
Initialize a file index.js
inside src
and dyte-api.js
inside utils
. Now let’s add our .env
file in src
, which will hold our API secrets.
Open up src/.env
file and add the following lines to it. Replace the placeholder values with the API secrets from the Dyte Dashboard.
DYTE_ORG_ID=<YOUR-DYTE-ORG-ID>
DYTE_API_KEY=<YOUR-DYTE-API-KEY>
We can start writing code now. Let’s start with creating axios
config for accessing Dyte APIs. Open up utils/dyte-api.js
and put in the following code.
This code will help to communicate with Dyte APIs and authentication.
const axios = require('axios');
require('dotenv').config();
const DYTE_API_KEY = process.env.DYTE_API_KEY;
const DYTE_ORG_ID = process.env.DYTE_ORG_ID;
const API_HASH = Buffer.from(
`${DYTE_ORG_ID}:${DYTE_API_KEY}`,
'utf-8'
).toString('base64');
const DyteAPI = axios.create({
baseURL: 'https://api.cluster.dyte.in/v2',
headers: {
Authorization: `Basic ${API_HASH}`,
},
});
module.exports = DyteAPI;
Next, we will write the routes.
Our front end will communicate on these routes to create meetings and add participants to meetings.
Let's open up index.js
and add the following code snippet.👇
const express = require('express');
const cors = require('cors');
const DyteAPI = require('./utils/dyte-api')
const PORT = process.env.PORT || 3000;
const app = express();
app.use(cors("http://localhost:3001"));
app.use(express.json());
app.post('/meetings', async (req, res) => {
const { title } = req.body
const response = await DyteAPI.post('/meetings', {
title,
});
return res.status(response.status).json(response.data);
});
app.post('/meetings/:meetingId/participants', async (req, res) => {
const meetingId = req.params.meetingId
const { name, picture, preset_name } = req.body
const client_specific_id = `react-samples::${name.replaceAll(' ', '-')}-${Math.random().toString(36).substring(2, 7)}`;
const response = await DyteAPI.post(`/meetings/${meetingId}/participants`, {
name,
picture,
preset_name,
client_specific_id,
});
return res.status(response.status).json(response.data);
});
app.listen(PORT, () => {
console.log(`Started listening on ${PORT}...`)
});
Ta-da! 🎩✨ We did it!
Now, we'll finally try out our code sharing platform to collaborate while coding with our friends and teammates.
With our shiny new Code editor and Dyte meeting all set up, we’ll finally try out our platform!
To run the whole application locally:
Inside client type PORT=3001 npm start
Inside plugin type npm start
Inside server type PORT=3000 npm run dev
And there you have it, in-app video conferencing and collaboration with our own “Code Collaboration Plugin”.
🧑💻 You can try out the code sharing platform here.
Conclusion
🎉 Woohoo! You've made it to the end, my friend! I hope you've learned a thing or two today and had a good time following along the way.
Together, we've built up a snazzy live pair programming and code sharing platform, complete with in-app meetings, all with just a pinch of React and a dollop of Dyte. Talk about a recipe for success!
We’ve got your back with our plugin templates and powerful SDKs, making it a breeze to jump right in and build your own collaborative masterpieces, just like the one we cooked up together today.
So what are you waiting for? Head on over to Dyte.io and let your creative juices flow! Start building your own collaborative applications and you might just create the next big thing! 🚀
Subscribe to my newsletter
Read articles from Vishal Pratap Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Vishal Pratap Singh
Vishal Pratap Singh
Hey there 👋, This is Vishal, CTO here at Sarg. I and my team create some great content for Developer Tools and help them reach developers organically. 🧑💻 I've helped multiple DevTools startups to get more reach using Tweets, Blogs, Threads, Videos, and more! Before starting up, I worked in a Data Engineering Role, contributed to several impactful open-source projects, and ranked in global coding contests. When I am not working, I like to do Yoga 🧘♂️, Read 📖 or Play the Piano 🎹