Building a Live Code Sharing Platform With Dyte and React
Tags: javascript, tutorial, webdev
Published: 2023-04-14 18:17:16

## 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](http://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](http://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](http://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](https://dyte.io/) and then hit Start Building. On the next page, Sign in with Google or GitHub account to get your free [Dyte account](https://accounts.dyte.io/auth/login) 🎉. 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.

![Disable AirPlay Receiver](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nc1pz806sv5hrtna5yzc.jpg)

Just for reference, this is how our `final folder structure` would look like at the end of this blog.


![Folder Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emr7wgv413q3xe7fn2df.jpg)


We will go ahead and install `Dyte` CLI using the command below.

```bash
$ npm install -g @dytesdk/cli
```

Going ahead with the authorization part and selecting the organization with the following commands.

```bash
$ dyte auth login $ dyte auth org
```

For more information, visit [Dyte CLI Docs](https://docs.dyte.io/cli/getting-started).


![Dyte Developer Portal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pnkuhvz895wxlz568ce4.gif)


### 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.

```bash
$ 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.

```bash
$ npm install
```

Next, we will add a couple of dependencies by running the following command.

```bash
$ 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.

```bash
$ 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.

```javascript
<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.


![Testing Plugin from Localhost Dev](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tl7n8ahwf1pym6gxngs.gif)



### Step 5: Handling Code Changes

To work on handling the live code changes, let's first create a new state named `code`

```javascript
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.

```javascript
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){}`

```javascript
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. 👇

```javascript
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](http://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.


![Plugin Preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znmp45ppgtsxozke4o24.gif)


### 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.

```bash
$ 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.

```bash
$ npx create-react-app .
```

Next, let us install the dependencies of `Dyte` and `code-editor` by running the following command:

```bash
$ npm i @dytesdk/react-ui-kit @dytesdk/react-web-core react-simple-code-editor
```

🎬 Now, let’s start our development server with npm start:

```bash
$ 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 👇.

```javascript
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.

```javascript
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`

```javascript
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 🏋🏼‍♂️.

```javascript
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:

```bash
$ mkdir server && cd server
```

We’ll start with installing a couple of dependencies, cd into the 'server' directory, and use the following command.

```bash
$ 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.

```bash
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.

```json
"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.

```apache
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.

```javascript
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.👇

```javascript
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](https://dyte-code-editor.herokuapp.com/room/bbbf8c1f-5eee-4548-90e6-54c1301711cb).


![Live Plugin Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lg0aityrzpxf61yaok7q.gif)



## 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](http://dyte.io/) and let your creative juices flow! Start building your own collaborative applications and you might just create the next big thing! 🚀


![Every developer's best companion — a coding cat](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlhcjo7g4af5sva2lquy.gif)