React code editor and syntax highlighter using PrismJS
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
Steps to implement code editor and highlight the syntax
- Create a React application
- Install dependencies
- Add code editor
- Highlight the syntax using Prism.js
- 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.
1 | 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.
1 | npm i prismjs react-simple-code-editor |
3. Add code editor
Let’s use the following sample code.
1 2 3 4 5 6 7 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 }} /> ); |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import { highlight, languages } from 'prismjs/components/prism-core'; import 'prismjs/components/prism-clike'; import 'prismjs/components/prism-javascript'; // ... // ... <div className='window'> <div className="title-bar"> <div className="title-buttons"> <div className="title-button"></div> <div className="title-button"></div> <div className="title-button"></div> </div> </div> <div className='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 }} /> </div> </div> |
Let’s import the following CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | 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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | .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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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 className="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 className='window'> <div className="title-bar"> <div className="title-buttons"> <div className="title-button"></div> <div className="title-button"></div> <div className="title-button"></div> </div> </div> <div className='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 }} /> </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..!! 🙂