# Content Editor - adding your own plug-in

## Introduction

The Content Editor [component](https://petal.build/components/content-editor) is currently based on [Editor.js](https://editorjs.io/). Editor.js is based on a plug-in eco-system. Petal Pro comes pre-installed with a [set of plug-ins](https://docs.petal.build/petal-pro-documentation/guides/pages/hwC3Cld8HvSX8nESZJp4#pre-installed-editor.js-plug-ins) that allow you to do common activities in a blog post (such as editing text, creating lists and tables, inserting images, etc).

However, what if you wanted to install your own Editor.js plug-in? There's nothing stopping you! There's only one extra thing to consider - the renderer needs to be adjusted to allow for the data of the new plug-in.

Though this may seem daunting. Updating the renderer is surprisingly trivial! In fact, creating your own Editor.js plug-in is pretty easy too. The rest of this guide will show you how to create your own Editor.js plug-in and adjust the Content Editor renderer.

## The Idea

To start we need an idea for our plug-in.  How about we build Markdown plug-in? Let's come up with some requirements:

* User can paste their markdown text into a textarea
* Markdown text is saved unaltered in the Editor.js json
* Update the renderer to show html instead of markdown (using [Earmark](https://hexdocs.pm/earmark))

## The Folder Structure

Before we start, it may help to provide an overview of the Petal Pro folder structure with regards to Editor.js and the Content Editor component:

```
├── assets
│   ├── css
│   │   ├── editorjs.css
│   ├── js
│   │   ├── editorjs
│   │   │   ├── petal-image.js
│   │   ├── hooks
│   │   │   ├── editorjs-hook.js
│   ├── package.json
├── lib
│   ├── petal_pro_web
│   │   ├── components
│   │   │   ├── pro_components
│   │   │   │   ├── content_editor.ex
```

Moving through the files, top to bottom:

* `editorjs.css` is for Petal Pro styles that apply to Editor.js
* `petal-image.js` is an Editor.js plug-in that we wrote for the [File Browser](/petal-pro-documentation/fundamentals/blog-cms.md#file-browser)
* `editorjs-hook.js` is the JavaScript hook
* The `assets` folder is where `package.json` is kept. This is where you would install your Editor.js plug-ins
* `content-editor.ex` is the component that uses the hook and where the renderer is

The plug-in that we're going to create will be placed adjacent to the `petal-image.js` file.

## Creating our Plug-in

Create a file called `/assets/js/editorjs/petal-markdown.js`:

{% code title="petal-markdown.js" %}

```javascript
export default class PetalMarkdown {
  static get toolbox() {
    return {
      title: "Markdown",
      icon: '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5" /></svg>',
    };
  }

  constructor({ data, block }) {
    this.data = data;
    this.block = block;
    this.wrapper = undefined;
  }

  render() {
    this.wrapper = document.createElement("div");

    const markdown_input = document.createElement("textarea");
    markdown_input.placeholder = "Paste your markdown here...";
    markdown_input.value = this.data.markdown || "";
    markdown_input.classList.add("petal-markdown");

    this.wrapper.append(markdown_input);

    return this.wrapper;
  }

  save(blockContent) {
    const markdown_input = blockContent.querySelector("textarea");

    return {
      markdown: markdown_input ? markdown_input.value : "",
    };
  }
}
```

{% endcode %}

We won't go into detail about the ins and outs of Editor.js. If you want to learn more, head on over to their [documentation](https://editorjs.io/the-first-plugin/). We'll just highlight the parts that are important to our new markdown feature.

The `toolbox()` static getter is used by Editor.js when showing the menu:

<figure><img src="/files/d20bKaeduuCOsSRdc9Mf" alt=""><figcaption><p>Editor.js menu</p></figcaption></figure>

The `render()` method is used to generate the `textarea` that's displayed in the editor. Note that the `petal-markdown` class is added to the `textarea` (this is referenced in the CSS below).&#x20;

Finally, the `save()` method is used to generate the output for the json document. It's an object with a single `markdown` property.

Add the following class to `/assets/css/editorjs.css`:

{% code title="editorjs.css" %}

```css
/* Add this to the bottom */

.codex-editor textarea.petal-markdown {
  @apply w-full rounded-md min-h-32 bg-gray-100 dark:bg-gray-950 dark:text-gray-400 border-gray-200 dark:border-gray-600 ring-0;
}

```

{% endcode %}

The last step is to configure our new plug-in within the js hook. Most of the code from this file has been cut to keep the contents short. But you only need to do two things - add the `import` and add the `tool`:

{% code title="editorjs-hook.js" %}

```javascript
import EditorJS from "@editorjs/editorjs";

// Add import
import PetalMarkdown from "../editorjs/petal-markdown";

const EditorJsHook = {
  mounted() {
    this.editor = new EditorJS({
      tools: {
        // Add tool
        petalMarkdown: PetalMarkdown,
      },
    });
  },
};

export default EditorJsHook;
```

{% endcode %}

Now in the editor, once you add a Markdown block, this is what our data entry looks like:

<figure><img src="/files/0EMK0C3DSeMddeGJYaeg" alt=""><figcaption><p>Data entry for markdown/plain text</p></figcaption></figure>

Now our plug-in is complete and the first two requirements are met:&#x20;

* The user can paste markdown text into a `textarea`; and&#x20;
* The data is saved in the output for the json.

## Updating the Renderer

The final step is to update the Content Editor so that it renders the markdown as HTML. If you look in:

```
/lib/petal_pro_web/components/pro_components/content_editor.js
```

You'll see that there's a function component called `content`:

```elixir
attr :json, :string, required: true

def content(assigns) do
  json_object = decode_json(assigns.json)

  blocks =
    case json_object do
      %{"blocks" => blocks} -> MapExt.atomize_keys(blocks)
      _ -> []
    end

  assigns = assign(assigns, :blocks, blocks)

  ~H"""
  <.block :for={block <- @blocks} block={block} />
  """
end
```

It decodes the Editor.js json and generates a list of `blocks`. It then calls the `.block` function component for each item in the array. The `.block` function uses pattern matching to render content for a plug-in. Here's what the `.block` function looks like for our new plug-in:

```elixir
defp block(%{block: %{type: "petalMarkdown", data: %{markdown: markdown}}} = assigns) do
  assigns = assign(assigns, :markdown, markdown)

  ~H"""
  <PetalProWeb.Markdown.pretty_markdown content={@markdown} />
  """
end
```

In `content_editor.ex`  this needs to be placed above the "catch all" `.block` function:

{% code title="content\_editor.ex" %}

```elixir
...

def content(assigns) do
  ...
end

attr :block, :map, required: true

...

defp block(%{block: %{type: "petalMarkdown", data: %{markdown: markdown}}} = assigns) do
  assigns = assign(assigns, :markdown, markdown)

  ~H"""
  <PetalProWeb.Markdown.pretty_markdown content={@markdown} />
  """
end

defp block(assigns) do
  ~H""
end

...
```

{% endcode %}

And with that, we're done! The third requirement is met:

* The Content Editor renderer has been adjusted for our new plug-in

## Installing a Third Party Plug-in

You can choose from any of the [available plug-ins.](https://github.com/editor-js/awesome-editorjs) Choose a plug-in and look for the npm command in the relevant README. For example, if you were to install the [checklist plug-in](https://github.com/editor-js/checklist), this is what you would do at the command line:

```shell
cd assets
npm add @editorjs/checklist
```

Then you'd have to configure the Editor.js hook:

```javascript
import EditorJS from "@editorjs/editorjs";

// Add import
import CheckList from '@editorjs/checklist';

const EditorJsHook = {
  mounted() {
    this.editor = new EditorJS({
      tools: {
        // Add tool
        checkList: CheckList,
      },
    });
  },
};

export default EditorJsHook;
```

However, you'd still have to update the Content Editor renderer.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.petal.build/petal-pro-documentation/guides/content-editor-adding-your-own-plug-in.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
