Tech

Next.js

【Next.js】MDX のコードブロックにシンタックスハイライトを適用する方法(コピー機能付き)

2025年9月21日

2025年9月21日

【Next.js】MDX のコードブロックにシンタックスハイライトを適用する方法(コピー機能付き)

こんにちは、けいこんぐらです!

今回は、Next.jsMDX のコードブロックにシンタックスハイライトを適用する方法について解説します。

実装方法の記事があまりなくて、なかなか苦労したので備忘録も兼ねて書いております、、

↓↓ 実現したいのは、いろんな技術記事でよく見かけるこれです 🔥

example.ts
const greeting = "Hello World";
console.log(greeting);

本ブログは Next.js で構成されており、記事部分には MDX を使用しています。

同じような構成で、自分のブログにコードブロックを実装したい方に向けて、私がどのように実装したかの参考になれば幸いです!⭐️

使用技術

  • Next.js
    • 本ブログは 15.5.2 を使用しています
    • Turbopack は使用していません
  • MDX
    • Next.js での詳細な使用方法は こちら をご覧ください
  • Tailwind CSS
  • shadcn/ui
  • rehype-pretty-code(今回の目玉 👀)
  • その他プラグイン

実装手順

まずは Next.js 公式の手順通りに MDX を導入します。

Terminal
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

MDX をきれいに表示するツールたちとシンタックスハイライトを適用するために、以下のパッケージをインストールします。

プラグイン役割
rehype-pretty-codeシンタックスハイライト
remark-gfmGitHub Flavored Markdown
react-children-utilitiesReact の子要素を操作する
@tailwindcss/typographyTailwind CSS の Typography プラグインで、MDX のスタイル調整
Terminal
npm install rehype-pretty-code remark-gfm react-children-utilities
npm install -D @tailwindcss/typography

next.config.tsnext.config.mjs に変更し、以下のように設定します。

設定できるテーマ一覧 → Themes

next.config.mjs
import createMDX from "@next/mdx";
import rehypePrettyCode from "rehype-pretty-code";
import remarkGfm from "remark-gfm";
 
/** @type {import('next').NextConfig} */
const nextConfig = {
  pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
};
 
/** @type {import('rehype-pretty-code').Options} */
const options = {
  // 🙆‍♂️ ここでテーマを指定できる
  theme: "dark-plus",
};
 
const withMDX = createMDX({
  options: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [[rehypePrettyCode, options]],
  },
});
 
export default withMDX(nextConfig);

プロジェクトルートに mdx-components.tsx を作成します。

mdx-components.tsx
import type { MDXComponents } from "mdx/types";
 
const components: MDXComponents = {};
 
export function useMDXComponents(): MDXComponents {
  return components;
}

忘れてはいけないのが Tailwind CSS の Typography プラグインの設定です ⚠️

globals.css
@import "tailwindcss";
@import "tw-animate-css";
 
@plugin "@tailwindcss/typography";
 
...省略
layout.tsx など
// 親要素に "prose" クラスを追加するとMDX要素がキレイに表示される!
 
...省略
 
return (
  <html lang="ja">
    <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
      <div className="prose">{children}</div>
    </body>
  </html>
);

これでとりあえずは、コードブロックにシンタックスハイライトが適用されるようになります!


これが、

app/page.mdx
```ts title="sample.ts"
const greeting = "Hello, Next.js with MDX!";
```

こうなる!いいですね 💯

実装結果1


あとは、mdx-components.tsx でコードブロックのスタイルを調整したり、コピー機能を追加したりしていきます。

まずは、コピーボタンを作ります!

ボタンがクリックされたら、props で渡されたテキストをクリップボードにコピーし、2 秒間チェックマークを表示するようなコンポーネントです。

components/copy-button.tsx
"use client";
 
import { Check, Copy } from "lucide-react";
import { useState } from "react";
import { cn } from "@/lib/utils";
// shadcn/ui の Button コンポーネントを使用しています
import { Button } from "./ui/button";
 
export default function CopyButton({
  text,
  className,
}: {
  text: string;
  className?: string;
}) {
  const [isOk, setIsOk] = useState(false);
 
  return (
    <Button
      size="icon"
      variant="ghost"
      className={cn("relative hover:bg-accent/10", className)}
      onClick={() => {
        setIsOk(true);
        setTimeout(() => setIsOk(false), 2000);
        navigator.clipboard.writeText(text);
      }}
    >
      <Check
        className={cn(
          "-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2",
          "text-muted-foreground transition-opacity duration-300",
          isOk ? "opacity-100" : "opacity-0"
        )}
      />
      <Copy
        className={cn(
          "-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2",
          "text-muted-foreground transition-opacity duration-300",
          isOk ? "opacity-0" : "opacity-100"
        )}
      />
      <span className="sr-only">コピー</span>
    </Button>
  );
}

あとはコードブロックで使用される要素をカスタマイズしていきます!(タイトルの装飾とコピーボタンの配置です)

mdx-components.tsx
import type { MDXComponents } from "mdx/types";
// onlyText は React の子要素からテキストだけを抽出するユーティリティ
import { onlyText } from "react-children-utilities";
import CopyButton from "./components/copy-button";
 
export const components = {
  figcaption: (props) => (
    <figcaption
      className="w-fit rounded-t-sm bg-primary px-3 py-1 text-primary-foreground text-xs"
      {...props}
    />
  ),
  pre: (props) => {
    return (
      <div className="relative">
        <pre
          className="mt-0 rounded-sm rounded-tl-none"
          {...props}
          tabIndex={-1}
        />
        <CopyButton
          text={onlyText(props.children)}
          className="absolute top-1 right-1"
        />
      </div>
    );
  },
} satisfies MDXComponents;
 
export function useMDXComponents(): MDXComponents {
  return components;
}

これで完璧です!よく見るやつですね!!🎉

実装結果2

おわりに

今回は、Next.js で MDX のコードブロックにシンタックスハイライトを適用する方法について解説しました。

コードブロックを実装するのはまあまあめんどくさいですが、一度実装してしまえば使い回せますし、サイトの見栄えもかなり良くなるので、「いいな」とか「これほしかった!」と思った方はぜひ導入してみてください 🔥

また、rehype-pretty-code は他にもいろんな機能があるので、興味がある方は 公式ドキュメント をご覧ください。

  • 横スクロール
  • 行番号
  • ハイライト行の指定 など

参考にさせていただいた動画

プロフィール

Profile Icon

けいこんぐら

ITエンジニア

大阪在住のサラリーマン。👨‍💻

新卒未経験でエンジニアとして働き、システムの開発等を行っている。

いつか、つよつよエンジニアになれるように日々頑張っています!

タグ