
INTRODUCTION
SLATE is an excellent library for creating WYSIWYG editors in REACT, I find it superior toQUILL.
However, I had difficulties inserting editable BLOCKS with syntax highlighting for code.
Yes, there is an officialexample, but at least for me, it's not very clear.
Let's cut to the chase! Let's see the CODE!!!
Let's say you have an empty React project with typescript.
Install the dependencies:
npm install slate slate-react slate-history prismjs
inApp.tsx
functionApp(){consteditor=useMemo(()=>withHistory(withReact(createEditor())),[]);return(<Slateeditor={editor}initialValue={[{children:[{text:''}]}]}><Editablestyle={{backgroundColor:"lightgray"}}renderElement={({attributes,element,children})=><div{...attributes}>{children}</div>}renderLeaf={({attributes,children,leaf})=><span{...attributes}>{children}</span>}/></Slate>)}
On initialization of the "App" component
I create theeditor controller
and apply it to theSlate component.
Let's create the tokens for highlighting withPRISMJS
inApp.tsx
...typeBaseRangeCustom=BaseRange&{className:string}functiondecorateCode([node,path]:NodeEntry){constranges:BaseRangeCustom[]=[]// make sure it is an Slate Elementif(!Element.isElement(node))returnranges// transform the Element into a stringconsttext=Node.string(node)// create "tokens" with "prismjs" and put them in "ranges"consttokens=Prism.tokenize(text,Prism.languages.javascript);letstart=0;for(consttokenoftokens){constlength=token.length;constend=start+length;if(typeoftoken!=='string'){ranges.push({anchor:{path,offset:start},focus:{path,offset:end},className:`token${token.type}`,});}start=end;}// these will be found in "renderLeaf" in "leaf" and their "className" will be appliedreturnranges;}
This function receives a SLATENode.
I get the text of the "Node"
With the text, I create the "tokens" withPRISMJS.
I transform the "tokens" intoRange.
The "Ranges" have theclassName
property with the information for the highlight.
Finally, I apply the "Ranges" to the Slate component
I assign the function to thedecorate
property which is rendered withrenderLeaf
still inApp.tsx
...<Editablestyle={{backgroundColor:"lightgray"}}decorate={decorateCode}renderElement={({attributes,element,children})=><div{...attributes}>{children}</div>}renderLeaf={({attributes,children,leaf})=>// here I apply the className that I calculated in "decorateCode"<span{...attributes}className={leaf.className}>{children}</span>}/>...
The code ishere!
End.
Optimize the code
You will notice that the "decorateCode" function is called with every interaction.
Every time you press a key, it creates the tokens for all the lines!
To optimize, we use a cache.
Let's move the "decorateCode" function inside the "App" component
functionApp(){...constcacheMem=useRef<{text:string,ranges:BaseRange[]}[]>([])functiondecorateCode([node,path]:NodeEntry){// CACHE **************constranges:BaseRangeCustom[]=[]// make sure it is an Slate Elementif(!Element.isElement(node))returnranges// transform the Element into a stringconsttext=Node.string(node)// CACHE **************constindex=path[0]constcache=cacheMem.current[index]if(!!cache&&cache.text==text)returncache.ranges// CACHE **************// create "tokens" with "prismjs" and put them in "ranges"consttokens=Prism.tokenize(text,Prism.languages.javascript);letstart=0;for(consttokenoftokens){constlength=token.length;constend=start+length;if(typeoftoken!=='string'){ranges.push({anchor:{path,offset:start},focus:{path,offset:end},className:`token${token.type}`,});}start=end;}// CACHE **************cacheMem.current[index]={text,ranges}// CACHE **************// these will be found in "renderLeaf" in "leaf" and their "className" will be appliedreturnranges;}}
You can find the codehere!
Basically, if thePath of the Node (which is an index)
is present in the cache and the text is the same
it immediately returns the "ranges" from the cache without creating the "tokens".
Top comments(3)

- LocationIndia
- WorkFull stack dev
- Joined
This is a really helpful breakdown of how to implement syntax highlighting in a SLATE editor. The caching optimization is particularly valuable, as it prevents unnecessary re-calculations. 👍
For further actions, you may consider blocking this person and/orreporting abuse