NPM Package

EditorJS Vue Renderer

A lightweight Vue 3 component that renders Editor.js blocks as native Vue components. No v-html, full customization, XSS protection included.

Terminal
$npm install editorjs-vue-renderer
import EditorjsRenderer from 'editorjs-vue-renderer'

<EditorjsRenderer :editorjs-blocks="blocks" />
Year2024
RoleCreator
TypeOpen Source
PlatformNPM Package

Built With

Vue 3
TypeScript
Vite
Vitest
DOMPurify

The Problem

Editor.js is an excellent block-based editor that I use in all my projects - both for admin panels and user-generated content (like on Gameverse). It's clean, extensible, and easy to maintain.

However, Editor.js stores content as JSON blocks, not HTML. The official renderer converts these blocks to HTML strings, which means you have to use v-html to display them. This creates several problems:

  • Security risks: v-html is vulnerable to XSS attacks if content isn't properly sanitized
  • Limited styling: Hard to apply scoped styles or use CSS modules with injected HTML
  • No Vue reactivity: Can't use Vue features like components, directives, or event handlers inside the rendered content
  • SSR complications: String-based rendering can cause hydration mismatches

The Solution

I created a Vue component that renders Editor.js blocks as native Vue components. Each block type (paragraph, header, list, etc.) is rendered as a proper Vue component, giving you full control over styling and behavior.

1
Native Vue Components

Each block renders as a real Vue component, not injected HTML

2
XSS Protection

Built-in DOMPurify sanitization prevents injection attacks

3
Custom Blocks

Pass your own Vue components for custom or unsupported block types

4
SSR Compatible

Works seamlessly with Nuxt and other SSR setups

Basic Usage

Simply pass your Editor.js blocks array to the component

App.vue
<template>
    <EditorjsRenderer :editorjs-blocks="blocks" />
</template>

<script setup lang="ts">
import EditorjsRenderer from 'editorjs-vue-renderer';

const blocks = [
    {
        id: 'header-1',
        type: 'header',
        data: { text: 'Hello World', level: 2 }
    },
    {
        id: 'paragraph-1',
        type: 'paragraph',
        data: { text: 'Welcome to my blog!' }
    },
    {
        id: 'list-1',
        type: 'list',
        data: {
            style: 'unordered',
            items: ['First item', 'Second item', 'Third item']
        }
    },
];
</script>

Supported Blocks

1

Paragraph

Text paragraphs with inline formatting support

2

Header

Headings from H1 to H6 with customizable levels

3

List

Ordered and unordered lists with nested items

4

Image

Images with captions and optional styling

5

Quote

Block quotes with optional caption/author

6

Table

Tables with optional header row support

Custom Blocks

Override default blocks or add support for custom Editor.js plugins

CustomExample.vue
<template>
    <EditorjsRenderer
        :editorjs-blocks="blocks"
        :custom-blocks="customBlocks"
    />
</template>

<script setup>
import EditorjsRenderer from 'editorjs-vue-renderer';
import CustomQuote from './blocks/CustomQuote.vue';
import CustomEmbed from './blocks/CustomEmbed.vue';

// Override default blocks or add new ones
const customBlocks = {
    quote: CustomQuote,          // Override default quote
    youtube: CustomEmbed,        // Add custom youtube block
    gallery: defineAsyncComponent(
        () => import('./blocks/Gallery.vue')
    )
};
</script>