tech-book-labs
techniques

mdx-shiki-code-rendering

**MDX 技術記事のコードブロックを「VS Code 同等の精度で / dark mode 対応で / ファイル名タブ付きで / diff が一目で / 行ハイライト付き」で見せたい** 時に呼び出すべき skill。Shiki(VS Code と同じ TextMate grammar ベースのシンタックスハイライタ)の dual テーマ機能(light/dark を 1 回 build で両対応、CSS 変数で切替)、`title="file.ts"` でファイル名タブ、`{1,3-5}` 行ハイライト、` ```diff ` で `+` / `-` カラー、言語ヒント / inline code、Astro / Next.js / Docusaurus 等での組み込み設定。次のいずれかに該当する時に invoke する: (1) `astro.config.mjs` / `next.config.js` の `shikiConfig` を編集中、(2) MDX に大量のコードブロックを書く / それを綺麗に見せたい、(3) dark mode 対応のコード表示、(4) diff や行強調の表示要件。検証: shiki@1.x、Astro 6.x、2026-05-09。

MDX × Shiki コードレンダリング skill

検証: shiki@1.x / Astro 6.x の Shiki 統合 / MDX v3 検証日: 2026-05-09 対象: MDX で技術記事を書きながらコードブロックを「読み手にやさしく」見せたい時の実装

技術記事の 半分はコードブロック。コードの見た目で記事の信頼度が変わる。 Shiki は VSCode と同じシンタックスハイライタで、TextMate grammar ベースで言語の精度が高い。

まず詰まる 3 点

  1. shikiConfig.themes を入れ忘れて灰色の世界になる(デフォルトは指定なし)
  2. MDX に渡る remark プラグインと markdown 設定は別系統(Astro 6 では mdx({ shikiConfig }) を別途渡す)
  3. ファイル名タブの記法は処理系で違う(title="..." 派 vs :filename 派)

デュアルテーマ(light / dark 自動切替)

Astro 6 の MDX 統合での書き方:

import mdx from "@astrojs/mdx";

export default defineConfig({
  integrations: [
    mdx({
      shikiConfig: {
        themes: {
          light: "github-light",
          dark: "github-dark",
        },
        wrap: true,  // 長い行を折り返す(横スクロールを抑える)
      },
    }),
  ],
  markdown: {
    shikiConfig: {
      themes: { light: "github-light", dark: "github-dark" },
      wrap: true,
    },
  },
});

Astro は <pre style="--shiki-light: #...; --shiki-dark: #..."> の形で 両テーマ分の color CSS 変数を埋め込む。CSS 側で prefers-color-scheme に応じて変数を切り替えれば自動でダークモード対応:

@media (prefers-color-scheme: dark) {
  pre, code {
    color-scheme: dark;
  }
}

Shiki が出力する <pre style="--shiki-dark: ..."></pre> を Astro が darkmode 時に dark 変数で上書き表示する仕組み。class 切り替えで dark mode を制御するなら defaultColor: false + 自前 CSS で対応する。

ファイル名タブ

import { sql } from "./client";
export async function findUser(id: string) {
  return sql`SELECT * FROM users WHERE id = ${id}`;
}

上の例は title="src/lib/db.ts"info string(言語の後ろのスペースの後)に書いた形。Astro / Next.js / Docusaurus いずれもこの記法を概ねサポート。

別記法に js:filename.js 形式(コロン区切り)もあるが、MDX v3 ベースのサイトでは title="..." のほうが portable

行ハイライト

特定の行を強調する記法(rehype プラグインに依存):

import express from "express";
const app = express();
app.use((req, res, next) => {     // ← highlight (3-5)
  console.log(req.url);
  next();
});
app.get("/", (req, res) => res.send("ok"));
app.listen(3000);                  // ← highlight (8)

Astro 標準の MDX には行ハイライト記法は組み込まれていない(rehype プラグインが必要)。 rehype-pretty-codeexpressive-code をインストールするのが現実的。expressive-code は Astro 用の専用 integration があり、ファイル名タブ + 行ハイライト + 差分を統合的に扱える。

diff 表示

- const result = await fetch(url);
+ const result = await fetch(url, { signal: controller.signal });

``` の後ろに diff を指定 すると、+ / - の行が緑 / 赤で着色される。 複数言語を組み合わせたい場合は diff-ts のような派生 grammar を使う(処理系依存)。

inline code(ハイライト付き)

Shiki は inline code もハイライトできる:

インラインで {`const x: number = 1`} のように `const x: number = 1` と書ける。

Astro の <Code> コンポーネントを使えば確実:

---
import { Code } from "astro:components";
---

<Code code="const x: number = 1" lang="ts" inline />

inline code を多用する記事(API リファレンス系)では、専用 component を 1 つ作っておくと一貫性が出る。

言語ヒント(対応言語の指定)

Shiki は VSCode 互換の grammar を内蔵。よく使う言語:

言語指定子
TypeScriptts, typescript
JSX/TSXtsx, jsx
JSONjson, jsonc(コメント付き)
Shellsh, bash, zsh
環境ファイルdotenv
設定ファイルtoml, yaml, ini
図記法mermaid(レンダリング処理は別途必要)

txttext でハイライト無し。出力例 / ログ表示に向く。

よく詰まる落とし穴

  • mdx({ shikiConfig })markdown.shikiConfig を両方設定しないと、.md.mdx で見た目が違う — どちらにも同じ shikiConfig を渡す
  • CSS 変数の prefix を変えると Astro 標準のダークモード切替が壊れる--shiki- prefix は変えない
  • 大きいコードブロックで build が遅くなる — Shiki は build 時にレンダリングするので、巨大ファイル(数千行)を貼ると build が重くなる。抜粋 + 「全文は に分割
  • 言語指定子が間違うと無音で fallback するjsjavascript は OK、javasc は灰色になる(エラーにならない)
  • VSCode のシンタックスハイライトと若干違う — VSCode は別 grammar を併用していることがある

推奨セットアップ(Astro 6 MDX サイト)

import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";

const shikiConfig = {
  themes: { light: "github-light", dark: "github-dark" },
  wrap: true,
};

export default defineConfig({
  integrations: [
    mdx({
      shikiConfig,
      // 行ハイライト / 差分が欲しいなら expressive-code 等を別途追加
    }),
  ],
  markdown: { shikiConfig },
});

expressive-code を使う場合は astro-expressive-code integration を追加して mdx() を後にする(Astro 公式のドキュメントに従う)。

向く / 向かないケース

  • 向く: MDX/MD ベースの技術記事、ドキュメントサイト、ブログ、書籍
  • 向かない: ランタイムで生成されるコード(LLM 出力等)→ shiki/wasm を browser ロードする選択肢あり、ただし bundle サイズに注意
  • 向かない: 非常に大きなコードブロック(数千行)→ link or embed gist を検討

See also(任意、単独でも完結)

本 skill は MDX × Shiki に特化しており、単独で完結します。記事化する場合は本リポの src/content/methodology/01-editorial-principles を参照。

参考