How to Implement Prism.js for Code Highlighting in Next.js: A Step-by-Step Guide

Gopal AdhikariGopal Adhikari
8 min read

Prism.js is a beautiful and lightweight syntax highlighting library for the modern web. In this article, we will learn how to highlight code syntax on the web using Prism.js and Next.js.

Setting Up a Next.js Project

Prerequisites

Before you get started, make sure you have the following installed on your machine:

  • Node.js: Latest version of Node.js.

  • npm (Node Package Manager): This comes with Node.js. Alternatively, you can use yarn as a package manager.

  • A Next.js application: This article will not cover how to create a Next.js application.

Installing necessary packages

Once your Next.js application is set up, you need to install the necessary packages for Prism.js. Run the following command into install necessary packages:

npm install prismjs

With these steps, your Next.js project is now set up and ready for integrating Prism.js for code highlighting.

Integrating Prism.js with Next.js

To use Prism.js for highlighting your code on the web, the HTML must be fully loaded in the browser before applying the syntax highlighting.

Importing Prism.js in your Next.js project

As we have already installed Prism.js, we need to import it where code highlighting is required:

import prism from "prismjs";

Setting up Prism.js themes

Import the Prism.js library and the desired theme from the prismjs package:

import prism from "prismjs";
import "prismjs/themes/prism-okaidia.min.css";

Here are all the available themes you can choose from:

  • prism-coy.css

  • prism-coy.min.css

  • prism-dark.css

  • prism-dark.min.css

  • prism-funky.css

  • prism-funky.min.css

  • prism-okaidia.css

  • prism-okaidia.min.css

  • prism-solarizedlight.css

  • prism-solarizedlight.min.css

  • prism-tomorrow.css

  • prism-tomorrow.min.css

  • prism-twilight.css

  • prism-twilight.min.css

  • prism.css

  • prism.min.css

You can select any of these themes based on your preference.

If you want to implement prism-coy.css then you can import like this

import prism from "prismjs";
import "prismjs/themes/prism-coy.min.css"; // or import "prismjs/themes/prism-coy.css"

Highlighting Code in Next.js Components

To highlight the code on the web, we need to consider a few things:

  • The code must be within a code tag. A code tag inside a pre tag works even better.

  • A className must be provided to the pre tag or code tag according to the programming language to be highlighted, such as language-typescript, language-bash, lang-typescript, or lang-bash. You can use lang or language as your choice; both will work.

  • The respective language plugin must be imported.

  • All HTML should be fully loaded before invoking Prism.

Let's highlight a simple statement for typescript.

"use client";

import prism from "prismjs";
import "prismjs/themes/prism-okaidia.min.css"; // theme for code appeareance
import "prismjs/components/prism-typescript"; // this plugin will hightlight typescript code
import { useEffect } from "react";

export default function Page() {
  useEffect(() => {
    prism.highlightAll();
  }, []);

  return (
    <main>
      <section>
        <h1 className="text-3xl font-bold text-center mt-8">
          Step-by-Step Guide to Using Prism.js for Code Highlighting in
          Next.js
        </h1>

        <div className="max-w-4xl mx-auto mt-8">
          <pre className="language-typescript">
            <code>console.log(&quot;Hello, world!&quot;);</code>
          </pre>
        </div>
      </section>
    </main>
  );
}

The preview of this code highlight is:

How is it working?

  • First of all, Prism.js and the theme prism-okaidia.min.css are imported.

  • The language-typescript class is provided to the pre tag for better highlighting of TypeScript.

  • The TypeScript plugin prism-typescript is imported, which is responsible for highlighting typescript code.

  • A useEffect hook is used for invoking Prism, which ensures the HTML is fully loaded before highlighting the code.

Loading Different Languages and Plugins

Let's highlight code of C, bash and python code:

"use client";

import prism from "prismjs";
import "prismjs/themes/prism-okaidia.min.css"; // theme
import "prismjs/components/prism-c"; // plugin for C
import "prismjs/components/prism-bash"; // plugin for bash
import "prismjs/components/prism-python"; // plugin for python
import { useEffect } from "react";

export default function Page() {
  useEffect(() => {
    prism.highlightAll();
  }, []);

  const pythonCode = `print("Hello, World!")`;

  const cCode = `#include <stdio.h>

int main() {
  printf("Hello, World!");
  return 0;
}`;

  const bashCode = `echo "Hello, World!"`;

  return (
    <main>
      <section>
        <h1 className="text-3xl font-bold text-center mt-8">
          Step-by-Step Guide to Using Prism.js for Code Highlighting in
          Next.js
        </h1>

        <div className="max-w-4xl mx-auto mt-8">
          <p className="font-bold mt-4">C</p>
          <pre className="language-c">
            <code>{cCode}</code>
          </pre>

          <p className="font-bold mt-7">Python</p>
          <pre className="language-python">
            <code>{pythonCode}</code>
          </pre>

          <p className="font-bold mt-7">Bash</p>
          <pre className="language-bash">
            <code>{bashCode}</code>
          </pre>
        </div>
      </section>
    </main>
  );
}

The preview of code highlighting is shown below.

Note: Don't forget to import respective plugin for specific language support and to give respective className for the pre tag.

Server-Side Rendering Considerations

Considering server-side rendering, we can create a client component PrismHighlighter that executes the Prism.js highlighting function. This component can be called in the main page without converting the main page to a client component.

"use client";

import prism from "prismjs";
import "prismjs/themes/prism-okaidia.min.css"; // theme
import "prismjs/components/prism-c"; // plugin for C
import "prismjs/components/prism-bash"; // plugin for bash
import "prismjs/components/prism-python"; // plugin for python
import { useEffect } from "react";

export function PrismHightler() {
  useEffect(() => {
    prism.highlightAll();
  }, []);
  return null;
}

This function can now be called in the main page:

import { Suspense } from "react";
import dynamic from "next/dynamic";

const SyntaxHighlighter = dynamic(
    () =>
        import("@/components/SyntaxHighlighter").then(
            (mod) => mod.SyntaxHighlighter
        ),
    { ssr: false }
);

export default function Page() {
  const pythonCode = `print("Hello, World!")`;

  const cCode = `#include <stdio.h>

int main() {
  printf("Hello, World!");
  return 0;
}`;

  const bashCode = `echo "Hello, World!"`;

  return (
    <main>
      <section>
        <h1 className="text-3xl font-bold text-center mt-8">
          Step-by-Step Guide to Using Prism.js for Code Highlighting in
          Next.js
        </h1>

        <div className="max-w-4xl mx-auto mt-8">
          <p className="font-bold mt-4">C</p>
          <pre className="language-c">
            <code>{cCode}</code>
          </pre>

          <p className="font-bold mt-7">Python</p>
          <pre className="language-python">
            <code>{pythonCode}</code>
          </pre>

          <p className="font-bold mt-7">Bash</p>
          <pre className="language-bash">
            <code>{bashCode}</code>
          </pre>
        </div>
      </section>
      <Suspense>
        <PrismHightler />
      </Suspense>
    </main>
  );
}

This approach ensures proper syntax highlighting on the server side while keeping the main page as a server component.

Using useRef Hook

While prism.highlightAll() attempts to highlight all the code on the page, it can be inefficient as it checks every element to determine if it contains code. This blanket approach can lead to unnecessary processing, especially on pages with a lot of content.

To improve efficiency, we can use the useRef hook to target and highlight only specific code blocks. By focusing on precise sections, we avoid the overhead of scanning the entire page, making the highlighting process more efficient and streamlined. Let's modify the PrismHandler code to utilize the useRef hook for this purpose.

"use client";

import prism from "prismjs";
import "prismjs/themes/prism-okaidia.min.css"; // theme
import "prismjs/components/prism-c"; // plugin for C
import "prismjs/components/prism-bash"; // plugin for bash
import "prismjs/components/prism-python"; // plugin for python
import { ReactNode, useEffect, useRef } from "react";

type Props = {
  code: ReactNode;
  language: "c" | "python" | "bash";
};

export function PrismHightler({ code, language }: Props) {
  const ref = useRef<null | HTMLPreElement>(null);

  useEffect(() => {
    if (ref.current) prism.highlightElement(ref.current);
  }, []);

  return (
    <pre ref={ref} className={`language-${language}`}>
      <code>{code}</code>
    </pre>
  );
}

In the above code, we use the useRef hook to reference a specific pre tag. By leveraging the prism.highlightElement() function, we can efficiently highlight the code block. This approach ensures that only the targeted code block is processed, avoiding unnecessary overhead and improving performance.

Now we, can call this component in the main page like this

import { Suspense } from "react";
import dynamic from "next/dynamic";

const SyntaxHighlighter = dynamic(
    () =>
        import("@/components/SyntaxHighlighter").then(
            (mod) => mod.SyntaxHighlighter
        ),
    { ssr: false }
);

export default function Page() {
  const pythonCode = `print("Hello, World!")`;

  const cCode = `#include <stdio.h>

int main() {
  printf("Hello, World!");
  return 0;
}`;

  const bashCode = `echo "Hello, World!"`;

  return (
    <main>
      <section>
        <h1 className="text-3xl font-bold text-center mt-8">
          Step-by-Step Guide to Using Prism.js for Code Highlighting in
          Next.js
        </h1>

        <div className="max-w-4xl mx-auto mt-8">
          <p className="font-bold mt-4">C</p>
          <PrismHightler language="c" code={cCode} />

          <p className="font-bold mt-7">Python</p>
          <PrismHightler language="python" code={pythonCode} />

          <p className="font-bold mt-7">Bash</p>
          <Suspense>
             <PrismHightler language="bash" code={bashCode} />
           </Suspense>
        </div>
      </section>
    </main>
  );
}

This code also results same output as before but with better performance.

Styling and Customization

You can override the default style of your code block using !important in the CSS.

import { PrismHightler } from "@/components/PrismHightler";
import { Suspense } from "react";

export default function Page() {
  const pythonCode = `print("Hello, World!")`;

  const cCode = `#include <stdio.h>

int main() {
  printf("Hello, World!");
  return 0;
}`;

  const bashCode = `echo "Hello, World!"`;

  return (
    <main>
      <section>
        <h1 className="text-3xl font-bold text-center mt-8">
          Step-by-Step Guide to Using Prism.js for Code Highlighting in
          Next.js
        </h1>

        <div className="max-w-4xl mx-auto mt-8">
          <p className="font-bold mt-4">C</p>
          <pre className="language-c !bg-gray-900">
            <code>{cCode}</code>
          </pre>

          <p className="font-bold mt-7">Python</p>
          <pre className="language-python !bg-slate-950">
            <code>{pythonCode}</code>
          </pre>

          <p className="font-bold mt-7">Bash</p>
          <pre className="language-bash">
            <code>{bashCode}</code>
          </pre>
        </div>
      </section>
      <Suspense>
        <PrismHightler />
      </Suspense>
    </main>
  );
}

The preview of the overridden code is:

Common Issues and Troubleshooting

Highlighting might not work due to several factors such as:

  • Forgot to import theme: Ensure that you have imported the theme from Prism.js.

  • Mismatch of language plugin: There might be a chance that you imported one language plugin but expected to highlight another language.

  • Missing className in thepre tag: After importing the language plugin, make sure to give a className such as language-typescript to the pre tag of the code block.

  • Forgot invoking Prism: You may have forgotten to invoke Prism.js, which executes the highlighting process.

  • HTML not rendering: Before highlighting the code, ensure that the HTML is fully loaded.

Conclusion

This article provides a step-by-step guide to integrating Prism.js, a lightweight syntax highlighting library, with a Next.js project. It covers setting up a Next.js application, installing and importing Prism.js, using various themes, highlighting code in components, considering server-side rendering, and troubleshooting common issues. Customize your code blocks with themes and additional CSS to enhance your web application's code presentation.

0
Subscribe to my newsletter

Read articles from Gopal Adhikari directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gopal Adhikari
Gopal Adhikari

I am a web developer with interest in mobile app development and cloud.