Definition
This is the pattern to use when, on a screen where you only need to "view" text (a preview card, a blog embed, etc.), you want to avoid downloading heavy editor libraries at all. Editing isn't needed, yet if the editor code tags along, that screen gets slow.
Concretely, you export the document package split into an editor entry and a viewer entry (an entry = the starting file the consuming app imports). The editor entry contains the rich-text editing engine (e.g. ProseMirror), the drag-and-drop library (dnd-kit), and editing UI (chrome) like the toolbar; the viewer entry drops those. This split must be done at all three levels: the export list, the CSS, and the import graph (the dependency graph of which file imports which).
Why it's needed
If one package exports everything through a single entrance (barrel export — a way of re-exporting many modules at once from an index), preview cards, blog embeds, and SSR previews all drag in the entire editor. Even if the on-screen DOM is identical to a read-only editor, the import graph must actually differ so that tree-shaking (the optimization that automatically strips unused code from the bundle) works and the editor code drops out.
How it works
@pkg/viewersubpath +viewer.css(excluding the chrome/toolbar layer) — the consumer doesn't import the editor entry in a viewer-only app.- CI-verify 0 PM/dnd imports in the viewer module graph via lint or dependency-cruiser — a production analyze is essential to prevent the dev-HMR tree-shake illusion.
- Share the BlockRenderer via a readOnly prop — the DOM is the same as the editor, and only the import graph should differ so peek/gallery/embed LCP/TTI drop.
- Include the CSS entry in the publish manifest's
exports/files— if omitted, an unstyled viewer ships after npm install.
Practical application
- A bundle-size-budget CI (viewer gzip cap).
- Storybook: ViewerOnly vs Editable side-by-side.
- peek/gallery/embed import only the viewer entry (lint rule).
Trade-offs
- The cost of maintaining a dual entry vs the peek LCP/TTI gain.
- Tokens are shared, only chrome is excluded — needs a visual-parity test.
When not to use it
- If the package always exposes only the full editor, the split is unnecessary.
Common mistakes
- The tree-shake illusion in dev HMR → a production analyze is essential.
- CSS omitted from npm publish → an unstyled viewer for the consumer.
Related concepts
- [[webpack-splitchunks-vendor-shared-chunk]]
- [[css-cascade-layers-theming]]