Easily get started with a web editor with
CodeMirror 6 + Vue 3

#web_editor #codemirror6 #vue3 #developer

Published on April 18, 2025

Hi! I’m a frontend engineer who loves turning ideas into interactive web experiences. I enjoy working with Vue, React, and everything in between especially when it means creating intuitive, delightful UIs. I believe good frontend is about empathy, not just pixels.

Jaegeon.Jo

Frontend Engineer

Intro

For developers, an editor is more than just a tool it's a daily companion and a weapon of choice in their workflow. Yet despite using editors every single day, very few developers have actually built one from scratch.That's because editors are far more complex than they appear. They're not just about inputting text they manage cursor movement, real-time syntax highlighting, undo/redo functionality, keyboard shortcuts, and even real-time collaboration. In many ways, they're more like full-fledged software platforms.That’s why most teams build on top of well-established open-source editors. After evaluating several options, our team at Murfy chose CodeMirror 6 for its modular architecture and outstanding performance.

What is CodeMirror 6?

Building an editor involves much more than handling simple key inputs it requires managing state, updating the screen, and handling many behind-the-scenes tasks. To address these challenges, many open-source editors have emerged over the years, and among them, CodeMirror has earned a reputation as one of the most reliable text editors.CodeMirror 6 is a complete reimagining of the original, built from the ground up with a completely new architecture. Its most notable features are its modular structure and extension-based design, which make it highly customizable and flexible.
Core Concepts of CodeMirror 6
👉‍
EditorState
Manages the internal state of the editor, such as the document content and cursor position.

👉EditorView
Renders the EditorState to the screen, handling the visual presentation of the editor.

👉Extention
Everything from themes and keyboard shortcuts to syntax highlighting is added via extensions.
Thanks to this architecture, you can build a lightweight editor with just the essentials, or add custom features tailored to your needs.

Put simply, CodeMirror isn't a finished editor it's a toolkit for building your own.

Getting Started with a Web Editor Using CodeMirror 6 + Vue 3

We'll try using CodeMirror 6 in a basic Vue 3 setup.
It should be easy to follow along!
1. Project Setup
To quickly create a Vue project, follow these steps:
pnpm create vue@latest
pnpm i @codemirror/view @codemirror/state
2. Create the CodeMirror Editor Component
Create the file as shown below: src/components/CodeMirrorEditor.vue
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { basicSetup, EditorView } from "@codemirror/view";
import { EditorState } from "@codemirror/state";

const editorContainer = ref<HTMLDivElement | null>(null);

onMounted(() => {
  if (editorContainer.value) {
    const state = EditorState.create({
      doc: "Hello, CodeMirror 6!",
      extensions: [basicSetup],
    });

    new EditorView({
      state,
      parent: editorContainer.value,
    });
  }
});
</script>

<template>
  <div>
    <div ref="editorContainer" style="border: 1px solid #ccc; height: 400px" />
  </div>
</template>
Explanation
We use ref to access the DOM element, and create the EditorView inside the onMounted hook.
The basicSetup includes default settings such as keyboard shortcuts, scrolling, selection, and syntax highlighting.
For a more detailed list, you can check the official documentation. With this, you've completed the basic setup to render a CodeMirror 6 editor inside a Vue component.
3. Using the Component
Now you can use this component wherever you need it. For example, add it to src/App.vue:
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { basicSetup, EditorView } from "@codemirror/view";
import { EditorState } from "@codemirror/state";

const editorContainer = ref<HTMLDivElement | null>(null);

onMounted(() => {
  if (editorContainer.value) {
    const state = EditorState.create({
      doc: "Hello, CodeMirror 6!",
      extensions: [basicSetup],
    });

    new EditorView({
      state,
      parent: editorContainer.value,
    });
  }
});
</script>

<template>
  <div>
    <div ref="editorContainer" style="border: 1px solid #ccc; height: 400px" />
  </div>
</template>

Customizing the Editor

CodeMirror 6 is built almost entirely around extensions.
By combining existing extensions or creating your own, you can easily build a fully customized editor tailored to your needs.Let’s take a look at how to customize the editor — starting with the basics and moving on to building your own extensions.
👉 Combining Basic Extensions
1️⃣Changing the Default Theme: Let’s replace the default theme with a dark theme.
pnpm i @codemirror/theme-one-dark Then, add the following: components/CodeMirrorEditor.vue
import { oneDark } from '@codemirror/theme-one-dark'

const state = EditorState.create({
  doc: 'Hello, CodeMirror 6!',
  extensions: [basicSetup, oneDark],
})
🎨 Once added, the editor will switch to the One Dark theme.
2️⃣Adding Syntax Highlighting
If you want to write your document in Markdown format,you can add syntax highlighting support. pnpm i @codemirror/lang-markdown

After adding it,
import { markdown } from '@codemirror/lang-markdown'

const state = EditorState.create({
  doc: '# Hello Markdown',
  extensions: [basicSetup, oneDark, markdown()],
})
📝 It also supports various languages, such as @codemirror/lang-javascript and @codemirror/lang-tex.
👉 Creating a Custom Extension
In CodeMirror 6, you can define your own commands and keymaps to extend how the editor behaves.This time, we’ll create a custom extension that moves the cursor one word at a time.

🚩 Goal: When the user presses a shortcut key,the cursor should move from its current position to the next or previous word.
👉 Extension code:
import { keymap } from '@codemirror/view'
import { EditorSelection } from '@codemirror/state'

// Function to move the cursor to a word boundary
function moveCursorByWord(dir: 'forward' | 'backward') {
  return (view: any) => {
    const { state } = view
    const selection = state.selection.main

    // current cursor position

    let pos = selection.head

    // Full text of document
    const text = state.doc.toString()

    if (dir === 'forward') {
      // Find the first space after the current position
      const nextSpace = text.slice(pos).search(/\s/)
      if (nextSpace >= 0) {
        pos += nextSpace + 1
      } else {
        pos = text.length
      }
    } else {
      // Find the last space before the current position
      const beforeText = text.slice(0, pos)
      const lastSpace = beforeText.lastIndexOf(' ')
      if (lastSpace >= 0) {
        pos = lastSpace + 1
      } else {
        pos = 0
      }
    }

    view.dispatch({
      selection: EditorSelection.cursor(pos),
      scrollIntoView: true,
    })

    return true
  }
}

// Shortcut Key Mapping Extension
export const wordMovementKeymap = keymap.of([
  { key: 'Mod-ArrowRight', run: moveCursorByWord('forward') },
  { key: 'Mod-ArrowLeft', run: moveCursorByWord('backward') },
])
👉 How to apply: Simply add wordMovementKeymap to the extensions array when creating the editor.
const state = EditorState.create({
  doc: '# Hello, Markdown!',
  extensions: [
    basicSetup,
    oneDark,
    markdown(),
    wordMovementKeymap, // ← 커서 단어 이동 기능 추가
  ],
})
👉 Result
- Ctrl + → (or Cmd + → on Mac): Moves the cursor to the next word
- Ctrl + ←
(or Cmd + ← on Mac): Moves the cursor to the previous word
📝 Unlike the default keyboard behavior, this recognizes precise word boundaries based on the actual document content.

Wrapping Up

CodeMirror 6 can feel a bit overwhelming at first.
You need to clearly understand and work with its core structures like EditorState, EditorView, and Extension.But once you get used to this architecture, it becomes a powerful tool allowing you to add only the features you need and fully customize how the editor behaves.In this article, we walked through how to set up CodeMirror 6 in a Vue 3 environment, combine basic extensions, and even create a custom one from scratch. We hope this guide serves as a helpful starting point for anyone looking to build a web-based editor.Thank you so much for reading.
At Murfy, we’re constantly pushing forward to build smarter, more collaborative tools for researchers and teams. Murfy is being developed as a next-generation academic editor enabling real-time collaboration, document editing, and review in one seamless experience.
We’d love your continued support and interest.Thank you!