Examples

Image upload

Paste or drop an image and the editor hands its bytes to your saveAttachment callback, which stores them and returns the path to link. resolveImage maps that path back to a URL the page can load. Here the "upload" stays in the browser tab.

    import { createEditorState, mountEditor, type EditorHost } from "@openmarkdown-dev/editor";
    
    // Stand-in for your upload endpoint: keep the bytes in this tab and hand back a URL.
    // A real host would POST them and return the stored file's address.
    const uploaded = new Map<string, string>();
    let n = 0;
    
    const host: EditorHost = {
      async saveAttachment(_docPath, bytes, hint) {
        const name = `uploads/${++n}-${(hint ?? "image").replace(/[^\w.-]+/g, "-")}`;
        uploaded.set(name, URL.createObjectURL(new Blob([bytes.slice()])));
        const li = document.createElement("li");
        li.textContent = `${name} (${bytes.length} bytes)`;
        document.getElementById("uploads")!.append(li);
        return name; // inserted into the document as ![](uploads/…)
      },
      // Map what the document says to what the browser may load.
      resolveImage: (src) => uploaded.get(src) ?? (src.startsWith("https://") ? src : null),
    };
    
    mountEditor(
      document.getElementById("editor")!,
      createEditorState({
        doc: "# Paste an image\n\nCopy a screenshot and paste it here, or drop an image file. Drag its corner to resize.\n\n",
        // A document path turns paste / drop on: attachments are named relative to it.
        docPath: "/notes/example.md",
        host,
      }),
    );
    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>OpenMarkdown editor: image upload</title>
        <style>
          #editor { height: 340px; border: 1px solid #d9d6cf; border-radius: 8px; overflow: hidden; }
          #editor .cm-editor { height: 100%; }
          #uploads { font: 13px ui-monospace, monospace; margin-top: 10px; min-height: 1.5em; }
        </style>
      </head>
      <body>
        <div id="editor"></div>
        <ol id="uploads" aria-label="Uploads"></ol>
        <script type="module" src="./main.ts"></script>
      </body>
    </html>
    {
      "name": "openmarkdown-editor-example-image-upload",
      "private": true,
      "type": "module",
      "scripts": {
        "dev": "vite",
        "build": "tsc && vite build",
        "preview": "vite preview"
      },
      "dependencies": {
        "@openmarkdown-dev/editor": "0.1.0"
      },
      "devDependencies": {
        "typescript": "~5.8.3",
        "vite": "^7.0.4"
      }
    }