A set of plugins and utils for integrating remark, mdast, and ProseMirror. Can be used to convert ProseMirror documents to Markdown, and vice versa!
npm i @handlewithcare/remark-prosemirror
This library exports tools for converting back and forth between Markdown and ProseMirror documents. It’s part of the unified ecosystem.
To parse a Markdown document into ProseMirror, first we parse the document into mdast (Markdown abstract syntax tree), and then use the remark plugin to compile that to ProseMirror.
import { unified } from "unified";
import { remarkParse } from "remark-parse";
import {
remarkProseMirror,
toPmNode,
toPmMark,
type RemarkProseMirrorOptions,
} from "@handlewithcare/remark-prosemirror";
import type { Node } from "prosemirror-model";
import { mySchema } from "./mySchema.js";
async function markdownToProseMirror(markdown: string): Node {
const doc = await unified()
// Use remarkParse to parse the markdown string
.use(remarkParse)
// Convert to ProseMirror with the remarkProseMirror plugin.
// It takes the schema and a set of handlers, each of which
// maps an mdast node type to a ProseMirror node (or nodes)
.use(remarkProseMirror, {
schema: mySchema,
handlers: {
// For simple nodes, you can use the built-in toPmNode
// util
paragraph: toPmNode(mySchema.nodes.paragraph),
listItem: toPmNode(mySchema.nodes.list_item),
// If you need to take over control, you can write your
// own handler, which gets passed the mdast node, its
// parent, and the plugin state, which has helper methods
// for converting nodes from mdast to ProseMirror.
list(node, _, state) {
const children = state.all(node);
const nodeType = node.ordered
? mySchema.nodes.ordered_list
: mySchema.nodes.bullet_list;
return nodeType.createAndFill({}, children);
},
// You can also treat mdast nodes as ProseMirror marks
emphasis: toPmMark(mySchema.marks.em),
strong: toPmMark(mySchema.marks.strong),
// And you can set attrs on nodes or marks based on
// the mdast data
link: toPmMark(mySchema.marks.link, (node) => ({
href: node.url,
title: node.title,
})),
},
} satisfies RemarkProseMirrorOptions)
.parse(markdown);
return doc;
}
To serialize a ProseMirror document to Markdown, first we convert the ProseMirror document to mdast. Then we can use remark to serialize the mdast to a string.
import { unified } from "unified";
import { remarkStringify } from "remark-stringify";
import {
fromProseMirror,
fromPmNode,
fromPmMark,
} from "@handlewithcare/remark-prosemirror";
import type { Node } from "prosemirror-model";
import { mySchema } from "./mySchema.js";
function proseMirrorToMarkdown(doc: Node) {
// Convert to mdast with the fromProseMirror util.
// It takes a schema, a set of node handlers, and a
// set of mark handlers, each of which converts a
// ProseMirror node or mark to an mdast node.
const mdast = fromProseMirror(doc, {
schema: mySchema,
nodeHandlers: {
// Simple nodes can be converted with the fromPmNode
// util.
paragraph: fromPmNode("paragraph"),
list_item: fromPmNode("listItem"),
// You can set mdast node properties from the
// ProseMirror node or its attrs
ordered_list: fromPmNode("list", () => ({
ordered: true,
})),
bullet_list: fromPmNode("list", () => ({
ordered: false,
})),
},
markHandlers: {
// Simple marks can be converted with the fromPmMark
// util.
em: fromPmMark("emphasis"),
strong: fromPmMark("strong"),
// Again, mdast node properties can be set from the
// ProseMirror mark attrs
link: fromPmMark("link", (mark) => ({
url: mark.attrs["href"],
title: mark.attrs["title"],
})),
},
});
return unified().use(remarkStringify).stringify(mdast);
}
Type Parameter |
---|
PmNodes extends string |
PmMarks extends string |
markHandlers:
Partial
<Record
<PmMarks
,PmMarkHandler
>>
lib/mdast-util-from-prosemirror.ts:125
nodeHandlers:
Partial
<Record
<PmNodes
,PmNodeHandler
>>
lib/mdast-util-from-prosemirror.ts:124
schema:
Schema
<PmNodes
,PmMarks
>
lib/mdast-util-from-prosemirror.ts:123
handlers:
MdastHandlers
lib/mdast-util-to-prosemirror.ts:367
optional
htmlHandlers:HastHandlers
lib/mdast-util-to-prosemirror.ts:368
schema:
Schema
<any
,any
>
lib/mdast-util-to-prosemirror.ts:366
fromPmMark<
Type
>(type
,getAttrs
?):PmMarkHandler
Type Parameter |
---|
Type extends "blockquote" | "break" | "code" | "definition" | "delete" | "emphasis" | "footnoteDefinition" | "footnoteReference" | "heading" | "html" | "image" | "imageReference" | "inlineCode" | "link" | "linkReference" | "list" | "listItem" | "paragraph" | "strong" | "table" | "tableCell" | "tableRow" | "text" | "thematicBreak" | "yaml" | "root" |
Parameter | Type |
---|---|
type |
Type |
getAttrs ? |
(pmNode ) => Omit <Extract <Root , { type : Type ; }> | Extract <Blockquote , { type : Type ; }> | Extract <Break , { type : Type ; }> | Extract <Code , { type : Type ; }> | Extract <Definition , { type : Type ; }> | Extract <Delete , { type : Type ; }> | Extract <Emphasis , { type : Type ; }> | Extract <FootnoteDefinition , { type : Type ; }> | Extract <FootnoteReference , { type : Type ; }> | Extract <Heading , { type : Type ; }> | Extract <Html , { type : Type ; }> | Extract <Image , { type : Type ; }> | Extract <ImageReference , { type : Type ; }> | Extract <InlineCode , { type : Type ; }> | Extract <Link , { type : Type ; }> | Extract <LinkReference , { type : Type ; }> | Extract <List , { type : Type ; }> | Extract <ListItem , { type : Type ; }> | Extract <Paragraph , { type : Type ; }> | Extract <Strong , { type : Type ; }> | Extract <Table , { type : Type ; }> | Extract <TableCell , { type : Type ; }> | Extract <TableRow , { type : Type ; }> | Extract <Text , { type : Type ; }> | Extract <ThematicBreak , { type : Type ; }> | Extract <Yaml , { type : Type ; }>, "type" | "children" > |
PmMarkHandler
lib/mdast-util-from-prosemirror.ts:178
fromPmNode<
Type
>(type
,getAttrs
?):PmNodeHandler
Type Parameter |
---|
Type extends "blockquote" | "break" | "code" | "definition" | "delete" | "emphasis" | "footnoteDefinition" | "footnoteReference" | "heading" | "html" | "image" | "imageReference" | "inlineCode" | "link" | "linkReference" | "list" | "listItem" | "paragraph" | "strong" | "table" | "tableCell" | "tableRow" | "text" | "thematicBreak" | "yaml" | "root" |
Parameter | Type |
---|---|
type |
Type |
getAttrs ? |
(pmNode ) => Omit <Extract <Root , { type : Type ; }> | Extract <Blockquote , { type : Type ; }> | Extract <Break , { type : Type ; }> | Extract <Code , { type : Type ; }> | Extract <Definition , { type : Type ; }> | Extract <Delete , { type : Type ; }> | Extract <Emphasis , { type : Type ; }> | Extract <FootnoteDefinition , { type : Type ; }> | Extract <FootnoteReference , { type : Type ; }> | Extract <Heading , { type : Type ; }> | Extract <Html , { type : Type ; }> | Extract <Image , { type : Type ; }> | Extract <ImageReference , { type : Type ; }> | Extract <InlineCode , { type : Type ; }> | Extract <Link , { type : Type ; }> | Extract <LinkReference , { type : Type ; }> | Extract <List , { type : Type ; }> | Extract <ListItem , { type : Type ; }> | Extract <Paragraph , { type : Type ; }> | Extract <Strong , { type : Type ; }> | Extract <Table , { type : Type ; }> | Extract <TableCell , { type : Type ; }> | Extract <TableRow , { type : Type ; }> | Extract <Text , { type : Type ; }> | Extract <ThematicBreak , { type : Type ; }> | Extract <Yaml , { type : Type ; }>, "type" | "children" > |
PmNodeHandler
lib/mdast-util-from-prosemirror.ts:157
fromProseMirror<
PmNodes
,PmMarks
>(pmNode
,options
):MdastRoot
Type Parameter |
---|
PmNodes extends string |
PmMarks extends string |
Parameter | Type |
---|---|
pmNode |
Node |
options |
FromProseMirrorOptions <PmNodes , PmMarks > |
MdastRoot
lib/mdast-util-from-prosemirror.ts:128
remarkProseMirror(
this
, ...parameters
):undefined
|void
Parameter | Type |
---|---|
this |
Processor <undefined , undefined , undefined , undefined , undefined > |
...parameters |
[RemarkProseMirrorOptions ] |
undefined
| void
toPmMark<
MdastNode
>(markType
,getAttrs
?): (node
,_
,state
) =>Node
[]
Type Parameter |
---|
MdastNode extends Nodes |
Parameter | Type |
---|---|
markType |
MarkType |
getAttrs ? |
(mdastNode ) => null | Record <string , unknown > |
Function
Parameter | Type |
---|---|
node |
MdastNode |
_ |
Parent |
state |
State |
Node
[]
lib/mdast-util-to-prosemirror.ts:335
toPmNode<
MdastNode
>(nodeType
,getAttrs
?): (node
,_
,state
) =>null
|Node
Type Parameter |
---|
MdastNode extends Nodes |
Parameter | Type |
---|---|
nodeType |
NodeType |
getAttrs ? |
(mdastNode ) => null | Record <string , unknown > |
Function
Parameter | Type |
---|---|
node |
MdastNode |
_ |
Parent |
state |
State |
null
| Node
lib/mdast-util-to-prosemirror.ts:324
Reach out to Handle with Care! We're a product development collective with years of experience bringing excellent ideas to life. We love Markdown and ProseMirror, and we're always looking for new folks to work with!