Syntax highlighting fails in React applications when using the new highlightAll() method
See original GitHub issueDescribe the issue I am trying to use highlightjs on a React project, which uses Next.js (although I don’t think the issue is related). I have added highlight.js using npm (v106.0) and loaded in a style sheet for a theme.
I have a simple function component set up like this:
import hljs from "highlight.js";
function Post(props) {
useEffect(() => {
hljs.highlightAll();
}, []);
return (
<>
<pre>
<code> /* code blocks that i want to highlight */ </code>
</pre>
</>
);
}
This is using the new highlightAll()
method call, but when the component is loaded the highlighting fails to take effect, and the <pre><code>
block is left unstyled.
Expected behavior I would expect the code block to be styled and the language highlighted using highlightjs and the stylesheet I have selected.
Which language seems to have the issue? The error is not language specific, but all code blocks are failing to get instantiated and highlighted, and any styling applied what so ever.
What I think the issue is
I think the issue is that the new highlightAll()
is not compatible with React and the way that React shadow loads components. Line #772 makes a check if the dom has loaded, else it breaks out:
function highlightAll() {
// if we are called too early in the loading process
if (!domLoaded) { wantsHighlight = true; return; }
const blocks = document.querySelectorAll('pre code');
blocks.forEach(highlightElement);
}
The only place that domLoaded
is set to true is in the boot()
method:
function boot() {
domLoaded = true;
// if a highlight was requested before DOM was loaded, do now
if (wantsHighlight) highlightAll();
}
The problem is that the only place boot()
is called from is an event listener:
if (typeof window !== 'undefined' && window.addEventListener) {
window.addEventListener('DOMContentLoaded', boot, false);
}
Since React will not cause the fire of the DomContentLoaded
event when a user navigates to a new page, boot()
is never called to set this variable to true, and when I am calling highlightAll()
from my code it’s is exiting too early. The boot()
is also not exposed to be able to call directly from my code.
Workaround
For now, the solution is to call hljs.initHighlighting()
(Line 747) since that method does not make the domLoaded check.
But, this method is marked as deprecated, so a fix needs to be put in place to allow this highlighter to continue to work with React applications. Perhaps allow a true
parameter to be passed to force it to run the syntax highter code.
Issue Analytics
- State:
- Created 3 years ago
- Comments:32 (19 by maintainers)
I don’t think it would. The problem is it’s not guaranteed that the DOMContentLoaded event is fired when HighLight.js is being loaded. So defacto domLoaded would always be
false
.Taking the code from #3039, I would further adapt it to this, to make sure it works as expected even if the DOMContentLoaded event has been fired before Highlight.js was loaded.
I would adapt the code to remove the domLoaded variable altogether and only check the document readystate in the highlight all function.
I think this would work as expected and also simplify the changed code from #3039.
Would it be possible for me to propose my changes on the PR?
Thanks for trying to help! Knowledgeable people on other ecosystems is always good to have.