npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mpdev_ab/document-creator

v0.2.4

Published

A rich text editor Vue 3 component built on Tiptap. Drop-in replacement for TinyMCE with font selection, tables, image upload (base64), templates, and full formatting — outputs HTML.

Downloads

644

Readme

@mpdev_ab/document-creator

A rich text editor Vue 3 component built on Tiptap. Drop-in replacement for TinyMCE with font selection, tables, image upload (base64), templates, and full formatting — outputs HTML.

Install

npm install @mpdev_ab/document-creator

Peer dependencies

Your project needs Vue 3 and the Tiptap packages:

npm install vue@^3.3.0 @tiptap/core @tiptap/vue-3 @tiptap/pm @tiptap/starter-kit @tiptap/extension-text-style @tiptap/extension-font-family @tiptap/extension-image @tiptap/extension-table @tiptap/extension-table-row @tiptap/extension-table-cell @tiptap/extension-table-header @tiptap/extension-underline @tiptap/extension-subscript @tiptap/extension-superscript @tiptap/extension-text-align @tiptap/extension-link @tiptap/extension-color @tiptap/extension-placeholder

Basic usage

<template>
  <DocumentEditor
    v-model="content"
    @save="onSave"
  />
</template>

<script setup>
import { ref } from 'vue'
import { DocumentEditor } from '@mpdev_ab/document-creator'

const content = ref('')

function onSave(html) {
  // html is a string — send it to your API however you like
  console.log(html)
}
</script>

Nuxt 3

Create a plugin at plugins/document-creator.client.ts:

import { DocumentEditor } from '@mpdev_ab/document-creator'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.component('DocumentEditor', DocumentEditor)
})

Then use it in any page/component:

<template>
  <ClientOnly>
    <DocumentEditor v-model="content" @save="onSave" />
  </ClientOnly>
</template>

Wrap in <ClientOnly> since the editor requires the DOM.

Props

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | v-model | string | Yes | — | HTML content (two-way binding) | | templates | Template[] | No | [] | List of templates to show in the load modal | | templatesLoading | boolean | No | false | Shows loading state in the load templates modal | | image | ImageConfig | No | { maxSize: 5MB, allowedTypes: ['image/jpeg', 'image/png', 'image/gif'] } | Image upload constraints | | theme | Partial<ThemeConfig> | No | See defaults below | Tailwind class overrides | | placeholder | string | No | '' | Placeholder text when editor is empty | | readonly | boolean | No | false | Hides toolbar and disables editing |

Events

| Event | Payload | Description | |-------|---------|-------------| | update:modelValue | string | Emitted on every content change (used by v-model) | | save | string | Emitted with the HTML string when the user clicks Save | | saveTemplate | { name: string, type: 'document' \| 'snippet', content: string } | Emitted when the user saves a template | | loadTemplates | — | Emitted when the load templates modal opens — fetch your templates and pass them via the templates prop | | deleteTemplate | Template | Emitted when the user clicks delete on a template | | error | { message: string, detail: unknown } | Emitted on image upload errors |

How saving works

The editor does not make any API calls. When the user clicks Save, it emits the save event with the HTML string. Your app handles persistence:

<template>
  <DocumentEditor v-model="content" @save="onSave" />
</template>

<script setup>
import { ref } from 'vue'
import { DocumentEditor } from '@mpdev_ab/document-creator'
import { useMutation } from '@vue/apollo-composable'
import gql from 'graphql-tag'

const content = ref('')

const SAVE_DOCUMENT = gql`
  mutation SaveDocument($content: String!) {
    saveDocument(content: $content) {
      id
    }
  }
`

const { mutate } = useMutation(SAVE_DOCUMENT)

async function onSave(html) {
  await mutate({ content: html })
}
</script>

Templates

Templates are managed by your app. The editor emits events — you handle fetching, saving, and deleting.

<template>
  <DocumentEditor
    v-model="content"
    :templates="templates"
    :templates-loading="loading"
    @save="onSave"
    @load-templates="fetchTemplates"
    @save-template="onSaveTemplate"
    @delete-template="onDeleteTemplate"
  />
</template>

<script setup>
import { ref } from 'vue'
import { DocumentEditor } from '@mpdev_ab/document-creator'

const content = ref('')
const templates = ref([])
const loading = ref(false)

async function fetchTemplates() {
  loading.value = true
  // Fetch from your GraphQL API, REST, etc.
  const result = await yourApi.getTemplates()
  templates.value = result // must be { id, name, type, content }[]
  loading.value = false
}

async function onSave(html) {
  await yourApi.saveDocument(html)
}

async function onSaveTemplate(data) {
  // data = { name: string, type: 'document' | 'snippet', content: string }
  await yourApi.createTemplate(data)
  await fetchTemplates() // refresh the list
}

async function onDeleteTemplate(template) {
  // template = { id, name, type, content }
  await yourApi.deleteTemplate(template.id)
  await fetchTemplates() // refresh the list
}
</script>

Template object shape

interface Template {
  id: string | number
  name: string
  type: 'document' | 'snippet'
  content: string
}

Template types:

  • document — replaces the entire editor content when loaded
  • snippet — inserts at the cursor position when loaded

ImageConfig

const imageConfig = {
  maxSize: 10 * 1024 * 1024,  // 10MB (default: 5MB)
  allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
}

Images are converted to base64 and stored inline in the HTML. After inserting, users can resize images using preset buttons (25%, 50%, 75%, 100%) or enter a custom width.

ThemeConfig

Override any part of the default Tailwind classes:

<DocumentEditor
  v-model="content"
  :theme="{
    toolbar: 'bg-gray-900 border-b border-gray-700 p-2 flex flex-wrap items-center gap-1',
    toolbarButton: 'hover:bg-gray-700 rounded p-1.5 text-gray-300 cursor-pointer',
    toolbarButtonActive: 'bg-blue-900 text-blue-300',
    editor: 'bg-gray-800 text-white min-h-[400px] p-4 prose prose-invert max-w-none',
    dropdown: 'bg-gray-800 border border-gray-700 rounded shadow-lg py-1 z-50',
    modal: 'bg-gray-800 text-white rounded-lg shadow-xl p-6',
    saveButton: 'bg-blue-500 text-white rounded px-4 py-2 hover:bg-blue-600',
  }"
/>

Defaults: | Key | Default classes | |-----|----------------| | toolbar | bg-white border-b border-gray-200 p-2 flex flex-wrap items-center gap-1 | | toolbarButton | hover:bg-gray-100 rounded p-1.5 text-gray-600 cursor-pointer | | toolbarButtonActive | bg-blue-100 text-blue-600 | | editor | bg-white min-h-[400px] p-4 prose max-w-none focus:outline-none | | dropdown | bg-white border border-gray-200 rounded shadow-lg py-1 z-50 | | modal | bg-white rounded-lg shadow-xl p-6 | | saveButton | bg-blue-600 text-white rounded px-4 py-2 hover:bg-blue-700 |

useDocumentEditor composable

Access the editor instance from child components:

<script setup>
import { useDocumentEditor } from '@mpdev_ab/document-creator'

const { editor, getHTML, getText, setContent, focus, isEmpty } = useDocumentEditor()

// Get current HTML
const html = getHTML()

// Set content programmatically
setContent('<p>New content</p>')

// Check if empty
if (isEmpty()) {
  // ...
}
</script>

This must be called from a component that is a child of <DocumentEditor>.

Full example

<template>
  <DocumentEditor
    v-model="content"
    placeholder="Write your document..."
    :readonly="false"
    :templates="templates"
    :templates-loading="templatesLoading"
    :image="{
      maxSize: 10 * 1024 * 1024,
      allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
    }"
    @save="onSave"
    @save-template="onSaveTemplate"
    @load-templates="onLoadTemplates"
    @delete-template="onDeleteTemplate"
    @error="onError"
  />
</template>

<script setup>
import { ref } from 'vue'
import { DocumentEditor } from '@mpdev_ab/document-creator'

const content = ref('<p>Hello world</p>')
const templates = ref([])
const templatesLoading = ref(false)

function onSave(html) {
  // Send html to your backend
}

function onSaveTemplate(data) {
  // data = { name, type, content }
}

async function onLoadTemplates() {
  templatesLoading.value = true
  templates.value = await fetchTemplatesFromApi()
  templatesLoading.value = false
}

function onDeleteTemplate(template) {
  // template = { id, name, type, content }
}

function onError(error) {
  console.error(error.message, error.detail)
}
</script>

Features

  • Bold, italic, underline, strikethrough, subscript, superscript
  • Font family (web-safe fonts), font size, font color
  • Text alignment (left, center, right, justify)
  • Line height
  • Headings (H1-H6) and paragraph
  • Bullet lists, numbered lists, blockquotes, horizontal rules
  • Links with URL input
  • Image upload (base64) with in-editor resize (25/50/75/100% presets + custom width)
  • Tables with Google Docs-style grid picker, row/column operations, merge/split cells, header toggle, cell background color, column resize
  • Video embed (YouTube/Vimeo URL)
  • Save/load templates (full documents and snippets)
  • Readonly mode
  • Tailwind CSS theming via class string props