Skip to content

Packages & Bundle Size

The core package is the foundation of Domternal. It contains the editor engine, 47 built-in extensions, toolbar controller, bubble menu, floating menu, and 45 icons, all in a single package.

13 Nodes: Document, Text, Paragraph, Heading, Blockquote, CodeBlock, BulletList, OrderedList, ListItem, TaskList, TaskItem, HorizontalRule, HardBreak

9 Marks: Bold, Italic, Underline, Strike, Code, Link, TextStyle, Subscript, Superscript

25 Extensions: StarterKit, BaseKeymap, History, Dropcursor, Gapcursor, TrailingNode, ListKeymap, LinkPopover, BubbleMenu, FloatingMenu, Placeholder, CharacterCount, Typography, TextAlign, TextColor, FontFamily, FontSize, LineHeight, Highlight, Focus, UniqueID, Selection, SelectionDecoration, InvisibleChars, ClearFormatting

Toolbar: ToolbarController, 45 Phosphor icons, defaultIcons export

Utilities: positionFloating, positionFloatingOnce (powered by @floating-ui/dom)

Bundle size: ~38 KB (own code) + ~70 KB (ProseMirror)

Section titled “Bundle size: ~38 KB (own code) + ~70 KB (ProseMirror)”

Domternal’s own code is ~38 KB gzipped. The rest is ProseMirror, the document editing engine that every ProseMirror-based editor ships with.

Bundlephobia reports the total as 108.4 KB gzipped because @domternal/core includes ProseMirror as a dependency. When you install core, you get everything you need to run the editor, no additional packages required.

Other editors split this differently. For example, @tiptap/core shows ~28 KB gzipped on bundlephobia, but that’s only the engine without any extensions. You still need @tiptap/starter-kit, individual extension packages, and @tiptap/pm (ProseMirror). Similarly, ngx-tiptap is only a few KB, but it requires @tiptap/core and all its dependencies on top. Once you add everything needed for a working editor, the total is comparable or larger.

Domternal bundles it all in one package, so the bundlephobia number reflects everything. Everything is tree-shakeable, so you import only what you need and your bundler eliminates the rest. Here’s the full breakdown:

Component% of totalGzippedWhat it does
Domternal core (self)35.2%~38 KBEditor engine, 47 extensions, toolbar controller, bubble menu, floating menu, 45 icons
prosemirror-view25.7%~28 KBDOM rendering, cursor handling, input events, decorations
prosemirror-model11.7%~13 KBDocument schema, node/mark types, content parsing and serialization
prosemirror-transform8.2%~9 KBDocument transformations, steps, mapping, operational transform
linkifyjs5.1%~6 KBURL detection for automatic link creation
prosemirror-state3.2%~3 KBEditor state management, transactions, plugins
prosemirror-commands3.1%~3 KBBuilt-in editing commands (join, lift, split, select)
Other ProseMirror packages~7.8%~8 KBHistory, keymap, input rules, dropcursor, gapcursor, schema-list, tables

Bundle size composition of @domternal/core showing 35.2% own code and the rest ProseMirror dependencies

The 108 KB is the maximum possible size. In practice, your bundle will be smaller. Domternal is marked as sideEffects: false, which means your bundler (Vite, webpack, esbuild) automatically removes any code you don’t import. If you use Bold, Italic, and Paragraph but not CodeBlock, TaskList, or Typography, those extensions are eliminated from your final build. You only pay for what you use.


Domternal ships as 10 npm packages under the @domternal scope. Here’s what you need depending on your setup.

The only required package. @domternal/pm (ProseMirror) is included as a dependency, so you don’t need to install it separately. This is enough for a fully functional headless editor with vanilla JavaScript or TypeScript. You provide your own CSS.

PackageDescription
@domternal/coreEditor engine, 47 built-in extensions, toolbar controller, bubble menu, floating menu, 45 icons
@domternal/pmProseMirror re-exports (model, view, state, transform, commands, history, keymap, inputrules, dropcursor, gapcursor, schema-list, tables). Automatically installed with core

Optional for vanilla setups, required for Angular components. Provides ready-made editor styles so you don’t have to write them from scratch.

PackageDescription
@domternal/themeLight and dark editor themes, toolbar and bubble menu styles, 70+ CSS custom properties for customization

Requires core and theme. Provides ready-made Angular components that handle toolbar rendering, bubble menus, and state management automatically.

PackageDescription
@domternal/angular5 Angular components: editor, toolbar, bubble menu, floating menu (in progress), emoji picker. Signals, OnPush, standalone

Additional features available as separate packages. Each requires @domternal/core.

PackageWhat it adds
@domternal/extension-table4 nodes (Table, TableRow, TableCell, TableHeader), 18 commands (merge, split, resize, styling), cell toolbar
@domternal/extension-imageImage insertion via paste, drag-and-drop, and URL, with image bubble menu
@domternal/extension-emojiEmoji node, picker component, :shortcode: autocomplete suggestions
@domternal/extension-mentionMention node with autocomplete suggestion renderer
@domternal/extension-detailsCollapsible content blocks: 3 nodes (Details, DetailsSummary, DetailsContent)
@domternal/extension-code-block-lowlightSyntax-highlighted code blocks via lowlight integration

Tree-shaking is a build optimization where your bundler (Vite, webpack, esbuild) analyzes which code you actually import and removes everything else from the final bundle. If a function or class is never imported, it doesn’t end up in your production build.

Domternal is built for tree-shaking. Every package is marked as sideEffects: false, which tells your bundler that any unused export can be safely removed. Each extension is a standalone class with no side effects, so importing Bold doesn’t pull in CodeBlock, Table, or anything else you didn’t ask for.

When you write this:

import { Editor, Document, Text, Paragraph, Bold, Italic } from '@domternal/core';

Your bundler includes only Editor, Document, Text, Paragraph, Bold, and Italic. The other 41 extensions (headings, lists, code blocks, task lists, text alignment, font size, etc.), the toolbar controller, the 45 icons, and every other unused export are eliminated from your bundle. They exist in the package but never reach the browser.

When you write this:

import { Editor, StarterKit } from '@domternal/core';

StarterKit bundles ~26 extensions together (paragraphs, headings, lists, blockquotes, code blocks, history, and more), so all of those are included. Extensions not part of StarterKit (like TextColor, FontSize, Typography, Highlight) are still excluded.

The ~38 KB core engine size is the maximum when you import everything. Your actual bundle depends on what you use:

SetupDomternal code included
Editor + Document + Text + Paragraph + a few marksWell under 38 KB
StarterKit (~26 extensions)Roughly half of the 38 KB. Does not include ToolbarController, icons, BubbleMenu, FloatingMenu, TextColor, FontSize, FontFamily, LineHeight, Highlight, Typography, and other optional extensions
StarterKit + all optional extensions + toolbar + iconsFull 38 KB

ProseMirror is also tree-shakeable. The core ProseMirror packages (model, view, state, transform) are always required (~60 KB), but packages like prosemirror-history, prosemirror-tables, and prosemirror-gapcursor are only included if you use the corresponding extensions.

Why ProseMirror is a dependency (not bundled)

Section titled “Why ProseMirror is a dependency (not bundled)”

Domternal keeps ProseMirror as a regular dependency via @domternal/pm rather than bundling it into the dist. This ensures that if you use multiple @domternal packages (core + extension-table + extension-image), ProseMirror is shared and deduplicated by your package manager, not duplicated in each package.

Some editors bundle ProseMirror into their dist output, which can result in a smaller bundlephobia number but can lead to duplicate ProseMirror code when using multiple packages.