4

I want to include PrismJS in my Next.js Blog to highlight code in .md files. The markdown is processed in /lib/posts.js with remark and remark-html and passed as HTML to react-markdown

// /lib/posts.js

export async function getPostData(id) {
  const fullPath = path.join(postsDirectory, `${id}.md`);
  const fileContents = fs.readFileSync(fullPath, "utf8");

  const matterResult = matter(fileContents);

  const processedContent = await remark()
    .use(html)
    .process(matterResult.content);
  const contentHtml = processedContent.toString();

  return {
    id,
    contentHtml,
    ...matterResult.data,
  };
}

The contentHtml is fetched in [id].js where I use ReactMarkdown to render it. Here I am importing Prism use the CodeBlock function to highlight. I import prism.css in my _app.js next to my global.css

// /pages/_app.js

import Container from "../components/Container";
import "../styles/global.css";
import "../styles/prism.css";

export default ({ Component, pageProps }) => (
  <Container>
    <Component {...pageProps} />
  </Container>
);

// /pages/posts/[id].js

import { getAllPostIds, getPostData } from "../../lib/posts";
import ReactMarkdown from "react-markdown/with-html";
import Prism from "prismjs";

const CodeBlock = (language, values) => {
  return <Prism language={language}>{values}</Prism>;
};

export default function Post({ postData }) {
  return (
    <ReactMarkdown
      escapeHtml={false} // Dangerous if content is user-generated
      source={postData.contentHtml}
      renderers={{ code: CodeBlock }}
    />
  );
}

export async function getStaticPaths() {
  const paths = getAllPostIds();
  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const postData = await getPostData(params.id);
  return {
    props: {
      postData,
    },
  };
}

It is working when I enter the URL manually and press return. As in https://nextjs-blog-ivory-nine.vercel.app/posts/first-next-js-blog-devdiary But when I navigate from the Main page - https://nextjs-blog-ivory-nine.vercel.app - to the first post, the code won't be hightlighted.

Do you have any idea? Thanks in advance!

ckoala
  • 293
  • 4
  • 12

2 Answers2

13

I just created an example of this which might help. It uses:

https://github.com/leerob/nextjs-prism-markdown

leerob
  • 2,876
  • 1
  • 18
  • 38
3

Assuming you were able to use markdown using remark, but without Prism. Adding prism is then done in a few steps :

yarn add remark-prism

Then call the parser during static rendering

import remark from 'remark'
import html from 'remark-html'
import prism from 'remark-prism';

// ...
const parser = remark().use(html).use(prism)

Add css to the html head. This work is just css done by the user browser. I use this link : https://cdnjs.cloudflare.com/ajax/libs/prism-themes/1.9.0/prism-coy-without-shadows.min.css but there are other options.

import Head from 'next/head'

const Article = ({post}: ArticleProps) => {

  return (
    <div>
      <Head>
        <link href="https://cdnjs.cloudflare.com/ajax/libs/prism-themes/1.9.0/prism-coy-without-shadows.min.css" rel="stylesheet" />
      </Head>
      <article className={article.blogpost}>
         {/* ... */}
      </article>
    </div>
  )
}

export default Article

Take note that adding .use(prism) made SSR significantly slower ! It's a problem in dev mode, but not in html exported site.

Nicolas Zozol
  • 6,910
  • 3
  • 50
  • 74
  • 1
    Make sure to set sanitize to false in the above example. remark().use(prism).use(html, { sanitize: false }).process(markdown) – mattferderer Nov 07 '22 at 22:52