---
title: Cloning Medium with Parchment
---
To provide a consistent editing experience, you need both consistent data and predictable behaviors. The DOM unfortunately lacks both of these. The solution for modern editors is to maintain their own document model to represent their contents. [Parchment](https://github.com/quilljs/parchment/) is that solution for Quill. It is organized in its own codebase with its own API layer. Through Parchment you can customize the content and formats Quill recognizes, or add entirely new ones.
In this guide, we will use the building blocks provided by Parchment and Quill to replicate the editor on Medium. We will start with the bare bones of Quill, without any themes, extraneous modules, or formats. At this basic level, Quill only understands plain text. But by the end of this guide, links, videos, and even tweets will be understood.
### Groundwork
Let's start without even using Quill, with just a textarea and button, hooked up to a dummy event listener. We'll use jQuery for convenience throughout this guide, but neither Quill nor Parchment depends on this. We'll also add some basic styling, with the help of [Google Fonts](https://fonts.google.com/) and [Font Awesome](https://fontawesome.io/). None of this has anything to do with Quill or Parchment, so we'll move through quickly.
Text
` and `Text
` represent the same content, the former is invalid and it is part of Quill's optimization process to unwrap the ``. Similarly, once we add formatting, `Test
` and `Test
` are also invalid, as they are not the most compact representation. Because of these constraints, **Quill cannot support arbitrary DOM trees and HTML changes**. But as we will see, the consistency and predicability this structure provides enables us to easily build rich editing experiences. ### Basic Formatting We mentioned earlier that an Inline does not contribute formatting. This is the exception, rather than the rule, made for the base Inline class. The base Block blot works the same way for block level elements. To implement bold and italics, we need only to inherit from Inline, set the `blotName` and `tagName`, and register it with Quill. For a compelete reference of the signatures of inherited and static methods and variables, take a look at [Parchment](https://github.com/quilljs/parchment/). ```js let Inline = Quill.import('blots/inline'); class BoldBlot extends Inline {} BoldBlot.blotName = 'bold'; BoldBlot.tagName = 'strong'; class ItalicBlot extends Inline {} ItalicBlot.blotName = 'italic'; ItalicBlot.tagName = 'em'; Quill.register(BoldBlot); Quill.register(ItalicBlot); ``` We follow Medium's example here in using `strong` and `em` tags but you could just as well use `b` and `i` tags. The name of the blot will be used as the name of the format by Quill. By registering our blots, we can now use Quill's full API on our new formats: ```js Quill.register(BoldBlot); Quill.register(ItalicBlot); const quill = new Quill('#editor'); quill.insertText(0, 'Test', { bold: true }); quill.formatText(0, 4, 'italic', true); // If we named our italic blot "myitalic", we would call // quill.formatText(0, 4, 'myitalic', true); ``` Let's get rid of our dummy button handler and hook up the bold and italic buttons to Quill's [`format()`](/docs/api/#format). We will hardcode `true` to always add formatting for simplicity. In your application, you can use [`getFormat()`](/docs/api/#getformat) to retrieve the current formatting over a arbitrary range to decide whether to add or remove a format. The [Toolbar](/docs/modules/toolbar/) module implements this for Quill, and we will not reimplement it here. Open your developer console and try out Quill's [APIs](/docs/api/) on your new bold and italic formats! Make sure to set the context to the correct CodePen iframe to be able to access the `quill` variable in the demo.` tag.Try setting some text to H1, and in your console, run `quill.getContents()`. You will see our custom static `formats()` function at work. Make sure to set the context to the correct CodePen iframe to be able to access the `quill` variable in the demo. ### Dividers Now let's implement our first leaf Blot. While our previous Blot examples contribute formatting and implement `format()`, leaf Blots contribute content and implement `value()`. Leaf Blots can either be Text or Embed Blots, so our section divider will be an Embed. Once created, Embed Blots' value is immutable, requiring deletion and reinsertion to change the content at that location. Our methodology is similar to before, except we inherit from a BlockEmbed. Embed also exists under `blots/embed`, but is meant for inline level blots. We want the block level implementation instead for dividers. ```js let BlockEmbed = Quill.import('blots/block/embed'); class DividerBlot extends BlockEmbed {} DividerBlot.blotName = 'divider'; DividerBlot.tagName = 'hr'; ``` Our click handler calls [`insertEmbed()`](/docs/api/#insertembed), which does not as conveniently determine, save, and restore the user selection for us like [`format()`](/docs/api/#format) does, so we have to do a little more work to preserve selection ourselves. In addition, when we try to insert a BlockEmbed in the middle of the Block, Quill splits the Block for us. To make this behavior more clear, we will explicitly split the block oursevles by inserting a newline before inserting the divider. Take a look at the Babel tab in the CodePen for specifics. ### Images Images can be added with what we learned building the [Link](#links) and [Divider](#divider) blots. We will use an object for the value to show how this is supported. Our button handler to insert images will use a static value, so we are not distracted by tooltip UI code irrelevant to [Parchment](https://github.com/quilljs/parchment/), the focus of this guide. ```js let BlockEmbed = Quill.import('blots/block/embed'); class ImageBlot extends BlockEmbed { static create(value) { let node = super.create(); node.setAttribute('alt', value.alt); node.setAttribute('src', value.url); return node; } static value(node) { return { alt: node.getAttribute('alt'), url: node.getAttribute('src') }; } } ImageBlot.blotName = 'image'; ImageBlot.tagName = 'img'; ``` ### Videos We will implement videos in a similar way as we did [images](#images). We could use the HTML5 `