4

I have basic Prism.js functionality working with my Next.js site by doing the following things:

package.json
  "dependencies": {
   ...
    "next": "9.5.3",
    "react": "16.13.1",
    "typescript": "3.9.4",
    "prismjs": "1.22.0"
  },
  "devDependencies": {
    ...
    "@types/prismjs": "1.16.2"
  }
pages/_app.tsx
...
import "prismjs/themes/prism-tomorrow.css";
...
pages/blog-article.tsx
...
import Prism from "prismjs";
import "prismjs/components/prism-hcl";
import "prismjs/plugins/line-highlight/prism-line-highlight";
...

export default function BlogArticle(){
  useEffect(() => {
    if (typeof window !== 'undefined') {
      Prism.highlightAll();
    }
  }, []);

  return <div>
    <pre className="language-hcl" style={{marginTop: "1em"}}>
      <code>{`xxx`}</code>
   </pre>
 </div>
}

That all works fine and source code is hightlighted properly - the problem comes when I try to use the line-highlight plugin for Prism.

When I add the data-line attribute to the HTML, as following.

pages/blog-article.tsx
...
    <pre className="language-hcl" style={{marginTop: "1em"}} data-line={1}>
      <code>{`xxx`}</code>
   </pre>
...
I get the following error:
Warning: Text content did not match. Server: "xxx 
" Client: "xxx"

So, if I'm understanding right: this is an error from Next.js, telling me that my server-rendered and client-rendered content are different?

Looking at the error message, it looks like the plugin is adding a newline at the end of the content, but only during the server-render, not the client-render.

Note that other Prism plugins seem to work; I tried out the line-numbers plugin and it didn't cause this problem.

Is this a bug with Prism.js, the line-highlight plugin or am I doing something wrong?

Is there a workaround I can do to get it going?

Shorn
  • 19,077
  • 15
  • 90
  • 168

1 Answers1

1

Initially mount the <pre> element without the data-line attribute.

In the useEffect, before calling Prism.highlightAll(), imperatively set the data-line attribute:

'use client'
import React, { useEffect, useRef } from 'react'
import Prism from 'prismjs'
import 'prismjs/themes/prism-tomorrow.css'

export default function Code({
  code,
  language,
  lineNumbers,
}: {
  code: string
  language: string
  lineNumbers?: string
}) {
  const ref = useRef<HTMLPreElement>(null)
  useEffect(() => {
    lineNumbers && ref.current?.setAttribute('data-line', `${lineNumbers}`)
    Prism.highlightAll()
  }, [])

  return (
    <div className='Code line-numbers'>
      <pre ref={ref} id='12345'>
        <code className={`language-${language}`}>{code}</code>
      </pre>
    </div>
  )
}

Note: One downside is that the page paints with unstyled code blocks. A potential "flash of unstyled content".


Original answer:

Same. The only workarounds I've found so far are to build a custom line highlighter with CSS, such as:

Michael McGreal
  • 521
  • 4
  • 7