Clue Mediator

React code editor and syntax highlighter using PrismJS

📅May 11, 2022

In this react article, you will learn how to create a code editor and highlight the syntax using PrismJS. Prism.js is a lightweight, extensible syntax highlighter, built with modern web standards in mind.

Checkout more articles on ReactJS

Demo Application

Output - React code editor and syntax highlighter using PrismJS - Clue Mediator

Output - React code editor and syntax highlighter using PrismJS - Clue Mediator

Steps to implement code editor and highlight the syntax

  1. Create a React application
  2. Install dependencies
  3. Add code editor
  4. Highlight the syntax using Prism.js
  5. Output

File Structure

  • react-code-editor

    • node_modules

    • public

      • index.html
    • src

      • App.js
      • index.css
      • index.js
      • editor.css
      • prism-vsc-dark-plus.css
    • package-lock.json

    • package.json

    • README.md

1. Create a React application

Set up a React application using `create-react-app`. Run the following command to create a React app.

npx create-react-app react-code-editor

2. Install dependencies

Here we will use the react-simple-code-editor and prismjs NPM packages to create a demo. Run the following command to install the packages.

npm i prismjs react-simple-code-editor

3. Add code editor

Let’s use the following sample code.

const codeSnippet = `function add(a, b) {
  return a + b;
}

add(5, 10);
// Output: 15
`;

Next, we have to import the `react-simple-code-editor` to render the editor in the React component.

import Editor from 'react-simple-code-editor';

// ...
// ...

const [code, setCode] = useState(codeSnippet);

// ...
// ...

return (
  <editor value={code} onvaluechange="{code" ==""> setCode(code)}
    highlight={(code) => { }}
    padding={10}
    style={{
      fontFamily: '"Fira code", "Fira Mono", monospace',
      fontSize: 12
    }}
  />
);
</editor>

4. Highlight the syntax using Prism.js

After rendering the editor, we will highlight the syntax using Prism.js. Here, we are importing the core component and javascript component to highlight the syntax in JavaScript format.

Import the following code in your component. Here, we have wrapped the `Editor` with the title bar to design the editor.

import { highlight, languages } from 'prismjs/components/prism-core';
import 'prismjs/components/prism-clike';
import 'prismjs/components/prism-javascript';

// ...
// ...

<div class="window">
  <div class="title-bar">
    <div class="title-buttons">
      <div class="title-button"></div>
      <div class="title-button"></div>
      <div class="title-button"></div>
    </div>
  </div>
  <div class="editor_wrap">
    <editor value={code} onvaluechange="{code" ==""> setCode(code)}
      highlight={code => highlight(code, languages.js)}
      padding={10}
      style={{
        fontFamily: '"Fira code", "Fira Mono", monospace',
        fontSize: 12
      }}
    />
  </editor></div>
</div>

Let’s import the following CSS.

prism-vsc-dark-plus.css

pre[class*="language-"],
code[class*="language-"] {
	color: #d4d4d4;
	font-size: 13px;
	text-shadow: none;
	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
	direction: ltr;
	text-align: left;
	white-space: pre;
	word-spacing: normal;
	word-break: normal;
	line-height: 1.5;
	-moz-tab-size: 4;
	-o-tab-size: 4;
	tab-size: 4;
	-webkit-hyphens: none;
	-moz-hyphens: none;
	-ms-hyphens: none;
	hyphens: none;
}

pre[class*="language-"]::selection,
code[class*="language-"]::selection {
	text-shadow: none;
	background: #b3d4fc;
}

@media print {
	pre[class*="language-"],
	code[class*="language-"] {
		text-shadow: none;
	}
}

pre[class*="language-"] {
	padding: 1em;
	margin: .5em 0;
	overflow: auto;
	background: #1e1e1e;
}

:not(pre) > code[class*="language-"] {
	padding: .1em .3em;
	border-radius: .3em;
	color: #db4c69;
	background: #f9f2f4;
}
/*********************************************************
* Tokens
*/
.namespace {
	opacity: .7;
}

.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
	color: #6a9955;
}

.token.punctuation {
	color: #d4d4d4;
}

.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
	color: #b5cea8;
}

.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
	color: #ce9178;
}

.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
	color: #d4d4d4;
	background: #1e1e1e;
}

.token.atrule,
.token.attr-value,
.token.keyword {
	color: #c586c0;
}

.token.function {
	color: #dcdcaa;
}

.token.regex,
.token.important,
.token.variable {
	color: #d16969;
}

.token.important,
.token.bold {
	font-weight: bold;
}

.token.italic {
	font-style: italic;
}

.token.constant {
	color: #9CDCFE;
}

.token.class-name {
	color: #4EC9B0;
}

.token.parameter {
	color: #9CDCFE;
}

.token.interpolation {
	color: #9CDCFE;
}

.token.punctuation.interpolation-punctuation {
	color: #569cd6;
}

.token.boolean {
	color: #569cd6;
}

.token.property {
	color: #9cdcfe;
}

.token.selector {
	color: #d7ba7d;
}

.token.tag {
	color: #569cd6;
}

.token.attr-name {
	color: #9cdcfe;
}

.token.attr-value {
	color: #ce9178;
}

.token.entity {
	color: #4ec9b0;
	cursor: unset;
}

.token.namespace {
	color: #4ec9b0;
}
/*********************************************************
* Language Specific
*/
pre[class*="language-javascript"],
code[class*="language-javascript"] {
	color: #4ec9b0;
}

pre[class*="language-css"],
code[class*="language-css"] {
	color: #CE9178;
}

pre[class*="language-html"],
code[class*="language-html"] {
	color: #d4d4d4;
}

.language-html .token.punctuation {
	color: #808080;
}
/*********************************************************
* Line highlighting
*/
pre[data-line] {
	position: relative;
}

pre[class*="language-"] > code[class*="language-"] {
	position: relative;
	z-index: 1;
}

.line-highlight {
	position: absolute;
	left: 0;
	right: 0;
	padding: inherit 0;
	margin-top: 1em;
	background: #f7ebc6;
	box-shadow: inset 5px 0 0 #f7d87c;
	z-index: 0;
	pointer-events: none;
	line-height: inherit;
	white-space: pre;
}

editor.css

.window {
  position: relative;
  margin: 30px;
  border-radius: 6px;
  background-color: #1a1e22;
  color: #fff;
  width: calc(100% - 60px);
  overflow: hidden;
}

.title-bar {
  height: 40px;
  background-color: rgb(231 231 231 / 6%);
  position: relative;
}

.title-text {
  color: #afafaf;
  position: absolute;
  left: 80px;
  bottom: 0;
  margin: 0;
  padding: 7px 16px;
  font-size: 15px;
  min-width: 10px;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.title-text span {
  outline: none;
}

.title-text img {
  width: 20px;
  margin-right: 10px;
}

.title-buttons {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
}

.title-button {
  background-color: #3a3a3a;
  width: 10px;
  height: 10px;
  border-radius: 50px;
  margin: 0 5px;
}

.title-button:nth-child(1) {
  background-color: #ff5656;
}
.title-button:nth-child(2) {
  background-color: #ffbc6a;
}
.title-button:nth-child(3) {
  background-color: #67f772;
}

.editor_wrap {
  height: calc(100% - 60px);
  overflow-y: auto;
  padding: 10px;
}

.editor_wrap textarea {
  outline: none;
}

5. Output

Let’s put all code together and see how it looks.

App.js

import React, { useState } from 'react';
import Editor from 'react-simple-code-editor';

import { highlight, languages } from 'prismjs/components/prism-core';
import 'prismjs/components/prism-clike';
import 'prismjs/components/prism-javascript';

import './prism-vsc-dark-plus.css';
import './editor.css';

const codeSnippet = `function add(a, b) {
  return a + b;
}

add(5, 10);
// Output: 15
`;

function App() {

  const [code, setCode] = useState(codeSnippet);

  return (
    <div class="App">
      <h3>React code editor and syntax highlighter using PrismJS - <a href="https://www.cluemediator.com/" target="_blank" rel="noopener">Clue Mediator</a></h3>

      <div class="window">
        <div class="title-bar">
          <div class="title-buttons">
            <div class="title-button"></div>
            <div class="title-button"></div>
            <div class="title-button"></div>
          </div>
        </div>
        <div class="editor_wrap">
          <editor value={code} onvaluechange="{code" ==""> setCode(code)}
            highlight={code => highlight(code, languages.js)}
            padding={10}
            style={{
              fontFamily: '"Fira code", "Fira Mono", monospace',
              fontSize: 12
            }}
          />
        </editor></div>
      </div>

    </div>
  );
}

export default App;

Run the application and check the output in the browser.

I hope you find this article helpful.
Thank you for reading. Happy Coding..!! 🙂

Demo & Source Code

GitHub Repository StackBlitz Project