Slate's data model has been built with serialization in mind. Specifically, its text nodes are defined in a way that makes them easier to read at a glance, but also easy to serialize to common formats like HTML and Markdown.
And, because Slate uses plain JSON for its data, you can write serialization logic very easily.
Plaintext
For example, taking the value of an editor and returning plaintext:
Here we're taking the children nodes of an Editor as a nodes argument, and returning a plaintext representation where each top-level node is separated by a single \n new line character.
An opening paragraph...
A wise quote.
A closing paragraph!
Notice how the quote block isn't distinguishable in any way, that's because we're talking about plaintext. But you can serialize the data to anything you want—it's just JSON after all.
HTML
For example, here's a similar serialize function for HTML:
import escapeHtml from'escape-html'import { Node, Text } from'slate'constserialize= node => {if (Text.isText(node)) {returnescapeHtml(node.text) }constchildren=node.children.map(n =>serialize(n)).join('')switch (node.type) {case'quote':return`<blockquote><p>${children}</p></blockquote>`case'paragraph':return`<p>${children}</p>`case'link':return`<a href="${escapeHtml(node.url)}">${children}</a>`default:return children }}
This one is a bit more aware than the plaintext serializer above. It's actually recursive so that it can keep iterating deeper through a node's children until it gets to the leaf text nodes. And for each node it receives, it converts it to an HTML string.
It also takes a single node as input instead of an array, so if you passed in an editor like:
consteditor= { children: [ { type:'paragraph', children: [ { text:'An opening paragraph with a ' }, { type:'link', url:'https://example.com', children: [{ text:'link' }], }, { text:' in it.' }, ], }, { type:'quote', children: [{ text:'A wise quote.' }], }, { type:'paragraph', children: [{ text:'A closing paragraph!' }], }, ],// `Editor` objects also have other properties that are omitted here...}
You'd receive back (line breaks added for legibility):
<p>An opening paragraph with a <a href="https://example.com">link</a> in it.</p>
<blockquote><p>A wise quote.</p></blockquote>
<p>A closing paragraph!</p>
It's really that easy!
Deserializing
Another common use case in Slate is doing the reverse—deserializing. This is when you have some arbitrary input and want to convert it into a Slate-compabitable JSON structure. For example, when someone pastes HTML into your editor and you want to ensure it gets parsed with the proper formatting for your editor.
Slate has a built-in helper for this: the slate-hyperscript package.
The most common way to use slate-hyperscript is for writing JSX documents, for example when writing tests. You might use it like so:
/** @jsx jsx */import { jsx } from'slate-hyperscript'constinput= ( <fragment> <elementtype="paragraph">A line of text.</element> </fragment>)
And the JSX feature of your compiler (Babel, TypeScript, etc.) would turn that input variable into:
constinput= [ { type:'paragraph', children: [{ text:'A line of text.' }], },]
This is great for test cases, or places where you want to be able to write a lot of Slate objects in a very readable form.
However! This doesn't help with deserialization.
But slate-hyperscript isn't only for JSX. It's just a way to build trees of Slate content. Which happens to be exactly what you want to do when you're deserializing something like HTML.
For example, here's a deserialize function for HTML:
<p>An opening paragraph with a <ahref="https://example.com">link</a> in it.</p><blockquote><p>A wise quote.</p></blockquote><p>A closing paragraph!</p>