# Mounting and cleanup

Let your application own containers and storage while the editor owns input, selections, and runtime resources.

## Native DOM

`createMsfeEditor()` works directly on a regular web page. The example's `mount.ts` has no Vue dependency; its Vue component only supplies the documentation demo container.

### native/editor.vue

```vue
<script setup lang="ts">
import { onMounted, onBeforeUnmount, shallowRef } from "vue";
import type { MsfeEditor } from "@quirekit/msfe-core";
import { mountEditor } from "./mount";
import type { ExampleLocale } from "../shared/types";
const props = withDefaults(defineProps<{ /** Host language for initial content and accessible naming. */ locale?: ExampleLocale }>(), { locale: "zh-CN" });
const parent = shallowRef<HTMLElement | null>(null);
let editor: MsfeEditor | undefined;
// Vue is only this documentation page's host; mount.ts has no framework dependency.
onMounted(() => { if (parent.value) editor = mountEditor(parent.value, props.locale); });
onBeforeUnmount(() => editor?.destroy());
</script>
<template><div class="p-qk-surface"><div ref="parent" class="overflow-hidden rounded-qk-control border border-qk-border-subtle" /></div></template>

```

### native/mount.ts

```ts
import { createMsfeEditor, type MsfeEditor } from "@quirekit/msfe-core";
import { StarterKit } from "@quirekit/msfe-starter-kit";
import { Appearance } from "../shared/appearance";
import { exampleMessages } from "../shared/messages";
import type { ExampleLocale } from "../shared/types";

/** @param parent - Host-owned element. @param locale - Host language. @returns Editor that the host must destroy on unmount. */
export function mountEditor(parent: HTMLElement, locale: ExampleLocale = "zh-CN"): MsfeEditor {
  const text = exampleMessages[locale];
  return createMsfeEditor({ parent, markdown: text.nativeInitial, mode: "live",
    label: text.nativeEditor, extensions: [StarterKit, Appearance] });
}

```

### shared/appearance.ts

```ts
import { Extension } from "@quirekit/msfe-core";
import { EditorView } from "@quirekit/cm-view";
import { Prec } from "@quirekit/cm-state";
import { HighlightStyle, syntaxHighlighting } from "@quirekit/cm-language";
import { tags } from "@lezer/highlight";
import { themeVariableReferences as token } from "@quirekit/ui";

/** Consumer-owned theme; source semantics stay in the installed feature packages. */
export const Appearance = Extension.create({ name: "documentationAppearance",
  /** @returns Scoped typography, surfaces and syntax colors from the host design system. */
  addCodeMirrorExtensions() {
    return Prec.high([
      EditorView.theme({
        "&": { ...token.typography.body, color: token.color.text.primary, backgroundColor: token.color.surface.page, minHeight: "180px" },
        ".cm-scroller": { fontFamily: "inherit", lineHeight: "1.8" },
        ".cm-content": { padding: token.spacing.surface, caretColor: token.color.text.primary },
        ".cm-line": { padding: "0" },
        ".cm-gutters": { color: token.color.text.tertiary, backgroundColor: token.color.surface.subtle, border: "none" },
        ".cm-activeLine, .cm-activeLineGutter": { backgroundColor: "transparent" },
        ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { backgroundColor: token.color.selection.document },
        ".cm-cursor": { borderLeftColor: token.color.text.primary },
        ".msfe-h1": { ...token.typography.pageTitle },
        ".msfe-h2, .msfe-h3": { ...token.typography.sectionTitle },
        ".msfe-heading": { paddingBlock: token.spacing.inline },
        ".msfe-link": { color: token.color.text.accent },
        ".msfe-code-line, .msfe-inline-code": { ...token.typography.code },
        ".consumer-status": { verticalAlign: "middle" },
        "&.cm-focused": { outline: "none" },
      }),
      syntaxHighlighting(HighlightStyle.define([
        { tag: tags.heading, color: token.color.text.primary, fontWeight: "600" },
        { tag: tags.strong, fontWeight: "700" },
        { tag: tags.emphasis, fontStyle: "italic" },
        { tag: [tags.link, tags.url], color: token.color.text.accent },
        { tag: tags.processingInstruction, color: token.color.text.tertiary },
        { tag: tags.monospace, color: token.color.text.secondary },
      ])),
    ]);
  },
});

```

### shared/messages.ts

```ts
const chinese = {
  editor: "Markdown 示例编辑器", modes: "编辑器模式", source: "源码", live: "实时预览", locked: "只读",
  undo: "撤销", redo: "重做", reset: "重置", readMarkdown: "读取 Markdown", readingPreview: "阅读预览", insertStatus: "插入状态", done: "已完成", todo: "待处理",
  nativeEditor: "原生 DOM 示例编辑器", statusEditor: "自定义状态编辑器",
  initial: "## 把想法写下来\n\n在这里编辑 **Markdown**，也可以切换到源码。\n\n- [x] 保留 Markdown 原文\n- [ ] 试试修改这一行\n",
  nativeInitial: "## 原生 DOM 接入\n\n这个实例直接使用 `createMsfeEditor()`。\n",
  statusInitial: "## 自定义状态\n\n设计评审：[[todo]]\n\n点击状态按钮，然后读取 Markdown 或撤销。\n",
};
const english: { [Key in keyof typeof chinese]: typeof chinese[Key] } = {
  editor: "Markdown example editor", modes: "Editor mode", source: "Source", live: "Live", locked: "Locked",
  undo: "Undo", redo: "Redo", reset: "Reset", readMarkdown: "Read Markdown", readingPreview: "Reading preview", insertStatus: "Insert status", done: "Done", todo: "Pending",
  nativeEditor: "Native DOM example editor", statusEditor: "Custom status editor",
  initial: "## Write your ideas\n\nEdit **Markdown** here, or switch to source.\n\n- [x] Preserve the Markdown source\n- [ ] Try editing this line\n",
  nativeInitial: "## Native DOM integration\n\nThis instance uses `createMsfeEditor()` directly.\n",
  statusInitial: "## Custom status\n\nDesign review: [[todo]]\n\nClick the status, then read the Markdown or undo.\n",
};
export const exampleMessages = { "zh-CN": chinese, en: english };

```

### shared/types.ts

```ts
/** Example host language; independent of the documentation router. */
export type ExampleLocale = "zh-CN" | "en";

```

## Lifecycle boundaries

Create the editor when its DOM container is ready, and call `destroy()` before removing that container. Destruction releases editor plugins, listeners, and view resources. Do not recreate the instance on every framework render.

Each instance owns independent content, selection, and history. Instances may share extension definitions, but each instance owns its mutable state.

## Switching documents

`replaceDocument(markdown, documentId)` explicitly switches documents and resets history and selection. To merge external changes to the same document, use version-checked `applyChanges()`. See [Reading and saving](/en/guide/saving).

## Asynchronous work

A custom node's `load(attrs, signal)` may return a resource and `release()`. Respect cancellation after attributes change, views unmount, or the editor is destroyed; stale results will not become the current node's resource. Persistent business requests belong to the host application.

## DOM and host responsibilities

The runtime owns its editor nodes inside parent. Do not overwrite that area's innerHTML during framework updates. Use separate parents and accessible labels for multiple editors. Share immutable definitions, not mutable business instances.

If you call `createComponentHost()` directly rather than using an adapter, you also own its disposal. End the editor and mounted views before disposing the host to avoid orphaned portals.

## Route lifecycle

Save or preserve unsaved text, cancel document work, dispose the editor, then remove its container. Reopening creates an instance from saved Markdown. Mode changes that retain the same document and history should call setMode instead of recreating the editor.

Assign resources to the shortest suitable scope. See [Resources](/en/extensions/resources) for complete ownership and cancellation patterns.
