Packages & Bundle Size
@domternal/core
Section titled “@domternal/core”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.
What’s inside
Section titled “What’s inside”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 total | Gzipped | What it does |
|---|---|---|---|
| Domternal core (self) | 35.2% | ~38 KB | Editor engine, 47 extensions, toolbar controller, bubble menu, floating menu, 45 icons |
| prosemirror-view | 25.7% | ~28 KB | DOM rendering, cursor handling, input events, decorations |
| prosemirror-model | 11.7% | ~13 KB | Document schema, node/mark types, content parsing and serialization |
| prosemirror-transform | 8.2% | ~9 KB | Document transformations, steps, mapping, operational transform |
| linkifyjs | 5.1% | ~6 KB | URL detection for automatic link creation |
| prosemirror-state | 3.2% | ~3 KB | Editor state management, transactions, plugins |
| prosemirror-commands | 3.1% | ~3 KB | Built-in editing commands (join, lift, split, select) |
| Other ProseMirror packages | ~7.8% | ~8 KB | History, keymap, input rules, dropcursor, gapcursor, schema-list, tables |

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.
All packages
Section titled “All packages”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.
| Package | Description |
|---|---|
@domternal/core | Editor engine, 47 built-in extensions, toolbar controller, bubble menu, floating menu, 45 icons |
@domternal/pm | ProseMirror 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.
| Package | Description |
|---|---|
@domternal/theme | Light and dark editor themes, toolbar and bubble menu styles, 70+ CSS custom properties for customization |
Angular
Section titled “Angular”Requires core and theme. Provides ready-made Angular components that handle toolbar rendering, bubble menus, and state management automatically.
| Package | Description |
|---|---|
@domternal/angular | 5 Angular components: editor, toolbar, bubble menu, floating menu (in progress), emoji picker. Signals, OnPush, standalone |
Extensions
Section titled “Extensions”Additional features available as separate packages. Each requires @domternal/core.
| Package | What it adds |
|---|---|
@domternal/extension-table | 4 nodes (Table, TableRow, TableCell, TableHeader), 18 commands (merge, split, resize, styling), cell toolbar |
@domternal/extension-image | Image insertion via paste, drag-and-drop, and URL, with image bubble menu |
@domternal/extension-emoji | Emoji node, picker component, :shortcode: autocomplete suggestions |
@domternal/extension-mention | Mention node with autocomplete suggestion renderer |
@domternal/extension-details | Collapsible content blocks: 3 nodes (Details, DetailsSummary, DetailsContent) |
@domternal/extension-code-block-lowlight | Syntax-highlighted code blocks via lowlight integration |
Tree-shaking
Section titled “Tree-shaking”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.
How it works in practice
Section titled “How it works in practice”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.
What this means for bundle size
Section titled “What this means for bundle size”The ~38 KB core engine size is the maximum when you import everything. Your actual bundle depends on what you use:
| Setup | Domternal code included |
|---|---|
Editor + Document + Text + Paragraph + a few marks | Well 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 + icons | Full 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.