10 人が閲覧(直近 30 日) フレームワーク

CodeMirror 6の使い方|インストール・設定・React/Vue連携とMonacoとの違い【2026年10月】

CodeMirror 6の使い方|インストール・設定・React/Vue連携とMonacoとの違い【2026年10月】

CodeMirror 6は、ブラウザに組み込むコードエディタのJavaScriptライブラリです。2022年6月8日に6.0.0が公開され、2026年10月時点でも6系の更新が続いています(描画を担う @codemirror/view の最新は6.43.15)。この記事では、npmで入れてエディタを表示するところから、値の取得・言語の切り替え・テーマ・Tabキーの設定、React/Vueへの組み込み、CodeMirror 5からの書き換え、Monaco Editorとの選び方までを、手元で実行して動作を確かめたコードで説明します。動作確認はNode.js上のjsdom(ブラウザのDOMを模したライブラリ)で行っており、実ブラウザでの見た目や端末ごとの日本語入力の挙動は確認の対象外です。

まとめ:CodeMirror 6を使う前に知っておく要点

  • CodeMirror 6は機能ごとに分かれたパッケージの集まりです。状態を持つ @codemirror/state と画面を描く @codemirror/view が中核で、行番号や補完などは「拡張(extension)」として足します。
  • 最初は npm install codemirror @codemirror/lang-javascript と basicSetup で動かし、細かく削りたくなったら minimalSetup か個別の拡張へ移ります。
  • 値は view.state.doc.toString() で読み、書き換えは view.dispatch() にまとめます。CodeMirror 5の getValue()・setValue()・fromTextArea() はありません。
  • Reactではラッパーの @uiw/react-codemirror、Vue 3では vue-codemirror か vue-codemirror6 を使うのが早道です。自前で組み込むなら、Reactの後片付けで view.destroy() を必ず呼びます。
  • スマートフォンでも編集させるならCodeMirror 6を選びます。Monaco Editorは公式READMEがモバイル非対応と明記しています。VS Codeと同じ補完や型チェックをブラウザに持ち込みたいならMonacoが向きます。

CodeMirror 6とは:CodeMirror 5から変わった設計と現在の版

CodeMirrorはMarijn Haverbekeが開発するオープンソースのエディタで、ライセンスはMITです。6は5の改修ではなく作り直しで、APIの互換性はありません。

CodeMirror 5から6で変わった設計(モジュール分割・トランザクション)

大きな違いは3点です。1点目は配布形式で、5はUMD形式で <script> タグから直接読めましたが、6は @codemirror/* の複数パッケージに分かれ、Vite・Rollup・webpackなどのビルドを通して使う前提になりました(公式の移行ガイド)。

2点目は状態の扱いです。6では文書や選択範囲を持つ状態(EditorState)が不変のデータで、変更は「トランザクション」として view.dispatch() に渡し、新しい状態に置き換えます。5のように cm.replaceRange() などのメソッドで直接書き換える方式ではありません(System Guide)。

3点目はモバイル対応で、6は「スマートフォンではプラットフォーム標準の選択・編集機能を使う」と公式サイトに掲げています。

2026年10月時点の版とリポジトリの移転

CodeMirror 6は「6.x」という1つの版番号ではなく、パッケージごとに版が進みます。2026年10月28日にnpmレジストリで確認した最新版は次のとおりです。

パッケージ 最新版 公開日 役割
codemirror 6.0.2 2025-06-19 basicSetup・minimalSetupの詰め合わせ
@codemirror/view 6.43.15 2026-10-19 描画・入力処理
@codemirror/state 6.7.6 2026-09-22 文書・選択・設定の状態
@codemirror/language 6.13.1 2026-10-07 構文解析・ハイライトの土台
@codemirror/commands 6.11.1 2026-09-15 既定キーマップ・履歴
@codemirror/lang-javascript 6.2.5 2026-03-02 JavaScript/TypeScript/JSX

検索で見かける「codemirror 6.65.7」は、npmに実在する版です。ただし2022年7月20日に公開されたこの版は、package.jsonのホームページが https://codemirror.net/5/ を指すCodeMirror 5系の配布物で、CodeMirror 6ではありません。npmの codemirror パッケージの6系は6.0.0・6.0.1・6.0.2の3つだけで、5系の最新は5.65.21(2026年2月7日・version5 タグ)です。npm install codemirror は6.0.2を入れるため、5系を使い続けるなら codemirror@5 と明示します。

開発の拠点はGitHubから移っています。@codemirror/view や @codemirror/state のGitHubリポジトリには「This repository has moved to https://code.haverbeke.berlin/codemirror/view」と書かれ、不具合報告も code.haverbeke.berlin の課題一覧 が窓口です。

CodeMirror 6のインストールと初期表示

パッケージの入れ方そのものは通常のnpmと同じです。npmの基本はnpmとは?CLIとレジストリの役割・package.jsonとv12の変更点を解説で説明しています。

npmで入れるパッケージの選び方

最初に入れるのは、詰め合わせの codemirror と、編集したい言語のパッケージ1つです。codemirror は @codemirror/view・@codemirror/state などを依存として一緒に入れます。

npm install codemirror @codemirror/lang-javascript
# テーマや他の言語は必要になってから足す
npm install @codemirror/theme-one-dark @codemirror/lang-python

言語パッケージは @codemirror/lang-html・lang-css・lang-json・lang-sql・lang-markdown などがあります。専用パッケージの無い言語は、CodeMirror 5のモードを移植した @codemirror/legacy-modes から読み込み、StreamLanguage.define() で包んで使います。

JavaScript用エディタの初期表示コード

Viteなどのバンドラーを通したプロジェクトなら、次のコードでJavaScript用のエディタが出ます。HTML側に <div id="editor"></div> を置き、その要素が生成された後にコードを実行します。parent に渡した要素の中にエディタのDOMが作られます。

import {EditorView, basicSetup} from "codemirror"
import {javascript} from "@codemirror/lang-javascript"

const view = new EditorView({
  doc: 'console.log("hello")\n',
  extensions: [basicSetup, javascript()],
  parent: document.querySelector("#editor"),
})

ビルドの仕組みが無い静的なHTMLに入れる場合は、公式のRollupでのバンドル例のように、1つのJSファイルへまとめてから <script> で読みます。Rollupの設定はRollupとツリーシェイキングの仕組み|sideEffects・treeshake設定とnpm導入、Vite 8以降の位置づけが参考になります。

basicSetupとminimalSetupの中身とバンドルサイズ

basicSetup は行番号・折りたたみ・括弧の自動補完・入力補完・検索・複数選択などをまとめた拡張です。minimalSetup は特殊文字の表示・取り消し履歴・選択範囲の描画・既定のハイライト・既定キーマップだけに絞っています。basicSetup は引数で中身を変えられないため、一部だけ外したいときは basicSetupのソース(importと配列だけのファイル)を自分のコードへ写して削るよう、ソースのコメントで案内されています。

読み込むサイズの差を、2026年10月28日にesbuild 0.28.2(--bundle --minify --format=esm)で測りました。

構成 圧縮後 gzip後
basicSetup+lang-javascript 506KB 170KB
minimalSetup+lang-javascript 406KB 138KB
minimalSetupのみ 285KB 94KB

公式のバンドル例は、basic setupと言語1つで圧縮後約400KB(gzip後135KB)、最小構成で250KB(75KB)と説明しています。今回の実測値は公式の概算より約14〜27%大きくなりましたが、バンドラーや圧縮条件も異なるため、ページの表示速度を詰める場合は公式の数字でなく自分の構成で測ってください。入力補完や検索が要らない表示専用の場所なら、minimalSetup にするだけで約100KB(圧縮後)減ります。

CodeMirror 6の設定:値の取得・言語・テーマ・キー操作

言語・テーマ・キー操作などの機能設定は、主に extensions 配列に拡張を並べて行います。この章のコードは、jsdom上で @codemirror/view 6.43.15と @codemirror/state 6.7.6を使って実行し、出力を確かめたものです。

値の取得・書き換え・変更の検知

文書全体の文字列は view.state.doc.toString() で取り、書き換えは変更内容を dispatch() に渡します。変更のたびに処理したいときは EditorView.updateListener を拡張に加え、update.docChanged で本文の変更だけを拾います。

import {EditorView, basicSetup} from "codemirror"

const view = new EditorView({
  doc: 'console.log("hello")\n',
  extensions: [
    basicSetup,
    EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        console.log(update.state.doc.toString())
      }
    }),
  ],
  parent: document.querySelector("#editor"),
})

// 文書全体を置き換える(CodeMirror 5の setValue 相当)
view.dispatch({
  changes: {from: 0, to: view.state.doc.length, insert: "print('hi')\n"},
})

行単位で読むときは view.state.doc.line(1).text(行番号は1始まり)、行数は view.state.doc.lines です。選択範囲やカーソル位置は view.state.selection.main から読みます。

言語の追加と後からの切り替え(Compartment)

エディタを作り直さずに言語やタブ幅を変えたいときは、設定の一部を Compartment で区切っておき、reconfigure() で差し替えます。

import {EditorView, basicSetup} from "codemirror"
import {Compartment} from "@codemirror/state"
import {javascript} from "@codemirror/lang-javascript"
import {python} from "@codemirror/lang-python"

const language = new Compartment()

const view = new EditorView({
  extensions: [basicSetup, language.of(javascript())],
  parent: document.querySelector("#editor"),
})

// セレクトボックスの変更などでPythonに切り替える
view.dispatch({effects: language.reconfigure(python())})

扱う言語が多い場合は、@codemirror/language-data の languages(言語ごとの名前・拡張子・読み込み関数の一覧)を使うと、使う言語のパッケージだけを必要になった時点で読み込めます。

テーマとエディタの高さの設定

見た目は EditorView.theme() にCSSセレクタとスタイルの組を渡して作ります。"&" はエディタの外枠を指し、高さを固定してスクロールさせるには .cm-scroller に overflow: auto を指定します(公式のスタイル例)。

import {EditorView} from "@codemirror/view"
import {oneDark} from "@codemirror/theme-one-dark"

const fixedHeight = EditorView.theme({
  "&": {height: "300px", fontSize: "14px"},
  ".cm-scroller": {overflow: "auto"},
})

// 既成のダークテーマと組み合わせる
const extensions = [basicSetup, javascript(), oneDark, fixedHeight]

この例も basicSetup と javascript を読み込んだうえで、extensions を EditorView に渡して使います。自作のダークテーマでは、EditorView.theme({...}, {dark: true}) のように第2引数で暗い配色であることを伝えます。構文の色分けだけ変えたいときは、@codemirror/language の HighlightStyle.define() を syntaxHighlighting() に渡します。

Tabキーとショートカットの設定

CodeMirror 6は、既定ではTabキーでインデントしません。キーボードだけで操作する人がエディタから抜け出せなくなるのを避けるためで、W3Cのアクセシビリティ基準(WCAGの「キーボードトラップなし」)に合わせた意図的な設計です(公式のTab処理の例)。Tabでインデントさせるには indentWithTab を割り当てます。次の例は、作った extensions を new EditorView({extensions, parent}) に渡して使う設定の断片で、save はアプリ側で用意する保存処理です。

import {keymap} from "@codemirror/view"
import {Prec} from "@codemirror/state"
import {indentWithTab} from "@codemirror/commands"

const extensions = [
  basicSetup,
  keymap.of([indentWithTab]),
  // Mod は macOS で Cmd、それ以外で Ctrl
  Prec.highest(keymap.of([{
    key: "Mod-s",
    run: (view) => { save(view.state.doc.toString()); return true },
  }])),
]

Tabを割り当てた場合でも、Escキーを押した直後のTabはエディタが処理せず、フォーカス移動に使えます。この抜け道は画面のどこかで案内するよう公式は勧めています。同じキーに複数の処理があるときは優先度の高い順に実行され、true を返した時点で後続は止まります。Prec.highest() で包むと、basicSetup 側の割り当てより先に動きます。

読み取り専用にする2つの方法

CodeMirror 6は「編集できるか」を2つの設定に分けています(公式の読み取り専用の例)。EditorState.readOnly.of(true) は入力や貼り付けなど利用者の直接操作を止める設定で、プログラムからの dispatch() による変更は止めません(実行すると本文が書き換わりました)。本文要素は contenteditable="true" のままで、カーソルやキーボードでの選択・コピーが使えます。EditorView.editable.of(false) は本文要素を contenteditable="false" の普通の要素にし、フォーカスできなくなります。

まったく操作させない表示にするなら両方を指定します。そのうえでキーボード操作も受けたい場合は、EditorView.contentAttributes.of({tabindex: "0"}) を足してフォーカスできるようにします。色分け表示だけが目的なら、EditorView を使わず @lezer/highlight で直接ハイライトする方が軽いと公式は案内しています。

ReactへのCodeMirror 6の組み込み

ラッパーを使う方法と、useEffect で自分で生成する方法があります。以下はReact 19.3.1で動作を確かめています。ReactとVueのどちらで作るか迷っている段階なら、ReactとVue 3の違いを比較|どっちを選ぶべきか【React 19.3・Vue 3.5対応】を先に読むと判断しやすくなります。

@uiw/react-codemirrorによる値と変更イベントの連携

@uiw/react-codemirror(2026年10月時点の最新は4.25.12)は、value と onChange で通常の入力欄のように扱えるラッパーです。basicSetup が既定で入り、theme に "light"・"dark" か独自のテーマを渡せます。

npm install @uiw/react-codemirror @codemirror/lang-javascript
import {useState, useCallback} from "react"
import CodeMirror from "@uiw/react-codemirror"
import {javascript} from "@codemirror/lang-javascript"

// 描画のたびに作り直さないよう、コンポーネントの外で固定する
const extensions = [javascript({jsx: true})]

export default function App() {
  const [value, setValue] = useState("console.log(1)")
  const onChange = useCallback((val, viewUpdate) => setValue(val), [])
  return (
    <CodeMirror
      value={value}
      height="200px"
      extensions={extensions}
      onChange={onChange}
    />
  )
}

このほか editable・readOnly・placeholder のprops、生成直後の EditorView を受け取る onCreateEditor(view, state) があります。extensions や onChange が変わるたびにラッパーは拡張を全部設定し直します(4.25.12のソースで確認)。上の例のように配列はコンポーネントの外か useMemo、関数は useCallback で固定します。

useEffectによるエディタの生成と破棄

依存を増やしたくない場合は、要素を ref で受けて useEffect の中で EditorView を作ります。後片付けの関数で view.destroy() を呼ぶのが要点です。

import {useEffect, useRef} from "react"
import {EditorView, basicSetup} from "codemirror"
import {javascript} from "@codemirror/lang-javascript"

export function Editor({initialDoc, onChange}) {
  const parentRef = useRef(null)
  // 最新のonChangeを参照する(エディタは作り直さない)
  const onChangeRef = useRef(onChange)
  useEffect(() => { onChangeRef.current = onChange }, [onChange])

  useEffect(() => {
    const view = new EditorView({
      doc: initialDoc,
      extensions: [
        basicSetup,
        javascript(),
        EditorView.updateListener.of((u) => {
          if (u.docChanged) onChangeRef.current(u.state.doc.toString())
        }),
      ],
      parent: parentRef.current,
    })
    return () => view.destroy()
  }, [])

  return <div ref={parentRef} />
}

updateListener は生成時に渡した関数を持ち続けるため、onChange を直接呼ぶと、親が別の関数を渡し直しても最初の関数が呼ばれ続けます。上のように useRef 経由で最新の関数を呼べば、エディタを作り直さずに済みます。initialDoc は初期値としてだけ使い、後から変えたい場合は前章の dispatch() で本文を置き換えます。

開発時に <StrictMode> で包むと、Reactは useEffect を「実行→後片付け→再実行」と2回走らせます。jsdomで確かめたところ、上のコードではエディタが2回作られて1回破棄され、画面のエディタは1つでした。後片付けを書かないと、同じ要素の中にエディタが2つ並びました。

Vue向けラッパーの比較と連携例(vue-codemirror・vue-codemirror6)

Vue向けには名前の似た2つのラッパーがあります。どちらも v-model で本文を双方向に結び付けられます。

項目 vue-codemirror vue-codemirror6
作者 surmon-china logue
最新版(公開日) 6.1.1(2022-08-27) 1.7.0(2026-09-24)
対応Vue 3.x 2.7.14以上・3.3.4以上
コンポーネント名 Codemirror CodeMirror

vue-codemirror は利用例が多い一方、2022年8月から新しい版が出ていません。Vue 2.7の既存案件や、更新が続いているものを選びたい場合は vue-codemirror6 が候補です。vue-codemirror は次のように使います(Vue 3.5.44で動作確認)。

npm install codemirror vue-codemirror @codemirror/lang-javascript
<script setup>
import {ref, shallowRef} from "vue"
import {Codemirror} from "vue-codemirror"
import {javascript} from "@codemirror/lang-javascript"

const code = ref("console.log('Hello')")
const extensions = [javascript()]
const view = shallowRef()
const handleReady = (payload) => { view.value = payload.view }
</script>

<template>
  <Codemirror
    v-model="code"
    :style="{height: '300px'}"
    :indent-with-tab="true"
    :tab-size="2"
    :extensions="extensions"
    @ready="handleReady"
  />
</template>

@ready で受け取った view は通常の EditorView なので、前章の dispatch() や state.doc がそのまま使えます。view から本文を書き換えると、v-model の値にも反映されることを確かめました。EditorView は大きなオブジェクトのため、ref でなく shallowRef に入れます。

CodeMirror 5から6へ移行するときの書き換え表

5のメソッドは、6では状態の読み取り(view.state)か dispatch() のどちらかに置き換わります。公式の移行ガイドから、よく使うものを抜き出しました。

CodeMirror 5 CodeMirror 6
CodeMirror(el) new EditorView({parent: el})
cm.getValue() view.state.doc.toString()
cm.setValue(text) view.dispatch({changes: …})(全範囲を置換)
cm.getLine(n) view.state.doc.line(n + 1).text
cm.lineCount() view.state.doc.lines
cm.getCursor() view.state.selection.main.head
cm.setCursor(pos) view.dispatch({selection: {anchor: pos}})
cm.on("change", f) EditorView.updateListener
マーク(markText) デコレーション(Decoration)

行番号の数え方が変わる点に注意してください。5の getLine(n) は0始まり、6の doc.line(n) は1始まりです。位置も5の「行と文字」の組から、6では文書先頭からの文字数(オフセット)になります。

CodeMirror.fromTextArea() は6に用意されていません。作者は移行ガイドで「堅牢でなかったため提供しない」と説明し、代わりに次の最小形を示しています。textareaを隠してエディタを隣に置き、フォーム送信時に本文を書き戻す方式です。

function editorFromTextArea(textarea, extensions) {
  const view = new EditorView({doc: textarea.value, extensions})
  textarea.parentNode.insertBefore(view.dom, textarea)
  textarea.style.display = "none"
  if (textarea.form) textarea.form.addEventListener("submit", () => {
    textarea.value = view.state.doc.toString()
  })
  return view
}

CodeMirror 6とMonaco Editorの違いと選び方

Monaco Editorは、VS Codeのソースから生成されたエディタです(monaco-editorのREADME)。VS Code本体の版の動きはVS Codeの最新バージョンは1.142|確認方法・アップデート手順と版の履歴【2026年10月】で追っています。

比較項目 CodeMirror 6 Monaco Editor
最新版(2026-10) view 6.43.15 0.57.0
スマートフォン 対応を明記 非対応と明記
Web Worker basicSetupでは使わない 言語サービスが使う
機能の足し方 拡張を選んで足す 組み込み機能+Provider API
Reactラッパー @uiw/react-codemirror @monaco-editor/react
ライセンス MIT MIT

選び方ははっきりしています。スマートフォンやタブレットでも編集させる画面、フォームの一部やドキュメント内の小さなコード欄、ページの読み込みを軽くしたい画面ではCodeMirror 6を選びます。MonacoのREADMEはモバイルブラウザ対応について「No」と答えています。

逆に、ブラウザ上のIDEのようにTypeScriptの型チェックや定義ジャンプ、VS Codeと同じ操作感が要件に入っているならMonacoが向きます。ただしVS Codeの拡張機能はMonacoでも基本的に動きません(READMEのFAQ)。CodeMirror 6にも公式の言語サーバー接続パッケージ @codemirror/lsp-client(2026年10月時点で6.3.0)がありますが、使う言語サーバーの用意とブラウザからの通信経路は自分で組む必要があります。Monacoは言語サービスがWeb Workerを作るため、file:// で開いたページではWorkerを作れず警告が出ます。バンドラーでWorkerを読み込む設定も要るので、その手間は事前に見積もっておきます。

社内の管理画面や業務システムに、設定ファイルやSQLを編集する欄を組み込むときは、編集する人の端末と必要な補完の深さから選ぶのが確実です。こうした業務用・Webアプリ開発の相談も受け付けています。

CodeMirror 6の導入でつまずきやすい点と向かない場面

仮想スクロールによるページ内検索の制約

CodeMirror 6は、大きな文書では画面に見えている部分とその前後だけをDOMに描画します。公式ガイドはこれを「viewport」と呼び、見えていない範囲の座標は取得できないと説明しています(System GuideのViewport節)。このため、ブラウザの Ctrl+F では画面外の行がヒットしません。Zennも自社のMarkdownエディタでこの問題に当たり、描画範囲を広げる設定が無いため @codemirror/search の検索機能を組み込んで解決したと技術ブログで書いています。basicSetup には検索のキー割り当て(Mod-f)が含まれるので、利用者にはエディタ内の検索を使うよう案内します。

保存ショートカットとブラウザ標準動作の抑止

Ctrl+S(macOSは Cmd+S)は basicSetup に含まれないため、割り当てないとブラウザの「ページを保存」が開きます。前章の Prec.highest() の例のように自分で割り当て、処理の最後で true を返してブラウザ側の動作を止めます。

CodeMirror 6が向かない場面

入力欄に数行のコードを貼るだけで、色分けも行番号も要らないなら、<textarea> で足ります。minimalSetup だけでも圧縮後285KBを読み込むため、表示速度を優先するページでは過剰です。

CodeMirror 6に関するよくある質問

CodeMirrorとは何ですか?

ブラウザ上で動くコードエディタのJavaScriptライブラリです。行番号・構文の色分け・入力補完・検索などの機能を、Webページの中に部品として組み込めます。現行の系列は2022年6月に公開されたCodeMirror 6で、ライセンスはMITです。

CodeMirror 5と6の違いは何ですか?

6は作り直された別物で、APIの互換性はありません。パッケージが @codemirror/* に分かれてビルドが必要になり、本文の変更は dispatch() 経由になりました。主なメソッドの対応は本文の書き換え表にまとめています。

CodeMirror 6はReactで使えますか?

使えます。@uiw/react-codemirror を入れると、value と onChange を渡すだけで動きます。ラッパーを使わない場合は、useEffect の中で EditorView を作り、後片付けで view.destroy() を呼びます。

@codemirror/viewとは何ですか?

CodeMirror 6のうち、エディタの描画とキーボード・マウス入力を担うパッケージです。EditorView・keymap・lineNumbers などが入っており、文書や選択範囲の状態は @codemirror/state が持ちます。

CodeMirror 6の最新バージョンはいくつですか?

CodeMirror 6はパッケージごとに版が進み、2026年10月28日時点では @codemirror/view が6.43.15、@codemirror/state が6.7.6、詰め合わせの codemirror が6.0.2です。npmには「6.65.7」という版もありますが、中身はCodeMirror 5系の配布物なので、CodeMirror 6として入れる版には選ばないでください。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram では記事の解説動画を配信しています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  2. 2026.10.29 テックブログ ソニー生命の不正アクセス:38.5万件の対象確認と窓口、停止中の手続き
  3. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  4. 2026.01.30 コラム 税務調査は個人と法人で何が違う?担当部署・件数・調べられる点と追徴税額を比較【令和6事務年度】
  5. 2026.09.26 コラム 価格転嫁とは?価格交渉促進月間の調査に回答義務はあるか・取適法の協議記録まで

RELATED POSTS 関連記事

目次