3

I want to create a slate.js-based editor component that keeps it's state in markdown. Slate.js docs keep repeating how simple serializing and deserializing state into md should be, but they don't provide an actual way to do it.

I tried achieving such editor with remark-slate-transformer in a very straight-forward way , based on these two examples: remark-slate-transformer, slate:

import React, { useMemo, useState } from "react";
import { createEditor } from "slate";
import { Slate, Editable, withReact } from "slate-react";
import stringify from "remark-stringify";
import unified from "unified";
import markdownParser from "remark-parse";
import { remarkToSlate, slateToRemark } from "remark-slate-transformer";
import { withHistory } from "slate-history";

function markdown2slate(markdown) {
  const processor = unified().use(markdownParser).use(remarkToSlate);
  return processor.processSync(markdown).result;
}

function slate2markdown(slate) {
  const processor = unified().use(slateToRemark).use(stringify);
  const ast = processor.runSync({ type: "root", children: slate });
  return processor.stringify(ast);
}

export const App = () => {
  const editor = useMemo(() => withHistory(withReact(createEditor())), []);
  const [value, setValue] = useState("**initialText**");

  const onChange = (newVal) => {
    setValue(slate2markdown(newVal));
  };

  const editorValue = markdown2slate(value);

  return (
    <div className="wrapper">
      <Slate editor={editor} value={editorValue} onChange={onChange}>
        <Editable />
      </Slate>
    </div>
  );
};

export default App;

sandbox here

But this doesn't work very well. I expect the initial text to appear in bold, but it doesn't. The cursor keeps jumping back to position 0 on every keystroke. Also, when I delete the string (value becomes ''), the editor breaks.

What is the correct, hassle-free way of making an editor component with state stored as markdown?

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Michal Kurz
  • 1,592
  • 13
  • 41

1 Answers1

2

I'm not sure why you would absolutely want to store the editor state as Markdown, but this just cannot and will not work: you can't simply swap Slate internal state to something different than what it expects, its own object model, and expect it to work.

What you can do is to deserialize Markdown content into a Slate state object, feed that to the editor, let Slate do its thing while you edit and then serialize back to Markdown to do whatever you need to do with it, store it, send it, etc.

  • 1
    Yes, serializing and deserializing is exactly what I had in mind, and what the code I posted is trying to achieve. As far as I'm concerned, that' exactly what building a slatejs-based editor that keeps state in markdown means. I want to build an editor component that keeps state in md and uses slate internally to render the editor UI. – Michal Kurz Sep 13 '21 at 13:55
  • I think Luc's point is well taken - let the value you pass to `` be in slate's native format to avoid selection errors. Only serialize to a markdown version when you save to your database on form submit, for instance. And only deserialize when you are loading a new editor from saved data. Keep your `value` in Slate's format and only have a `stringValue` calculated after the fact for persisting the message. – VictorB Oct 07 '21 at 21:32