#web_editor #codemirror6 #vue3 #developer
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.
pnpm create vue@latest
pnpm i @codemirror/view @codemirror/state
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>
ref
to access the DOM element, and create the EditorView
inside the onMounted
hook.basicSetup
includes default settings such as keyboard shortcuts, scrolling, selection, and syntax highlighting.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>
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],
})
One Dark
theme.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()],
})
@codemirror/lang-javascript
and @codemirror/lang-tex
.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') },
])
wordMovementKeymap
to the extensions
array when creating the editor.const state = EditorState.create({
doc: '# Hello, Markdown!',
extensions: [
basicSetup,
oneDark,
markdown(),
wordMovementKeymap, // ← 커서 단어 이동 기능 추가
],
})
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.