Limitations and trade-offs
Semantic colors, not exact TextMate fidelity
Tokens carry one of 19 semantic types. Two different TextMate scopes that an engine theme might color differently can share a type and therefore a color. This is the deliberate cost of theme-independent caching, cross-engine themes, and CSS-only theme switching. Exact per-scope styles are a possible future extension; they are not part of the current additive 1.x semantic token model.
Adapter token boundaries differ
Adapters must agree on line count and concatenated line text, not on token segmentation. Shiki may split a line differently from Prism or lowlight. Semantic types can also differ for ambiguous constructs. Do not depend on a particular token count across engines.
ESM-only
There is no CommonJS build. Consumers must use an ESM bundler or Node-based SSR with ESM. Vue is the only required peer; engines are optional peers and must be installed for the adapter you select.
Development warnings
Warnings are runtime diagnostics, not a build-time guarantee. Vite folds globalThis.process?.env?.NODE_ENV for production builds; bundlers that only replace process.env.NODE_ENV may keep warnings enabled in production. The failure direction is safe: warnings never suppress highlighting or change rendered output.
Browser support
The runtime targets modern ESM browsers with CSS custom properties. IE11 is not supported.
Themes use light-dark() to switch schemes without re-highlighting. Browsers that support custom properties and @supports but not light-dark() — for example Safari before 17.5, Firefox before 120, or Chrome before 123 — can import the optional fallback after the base stylesheet:
import 'vue-syntax-highlighter/styles.css'
import 'vue-syntax-highlighter/styles/fallback.css'fallback.css is wrapped in @supports not (color: light-dark(...)), so it is a no-op in modern browsers and safe to import unconditionally. Explicit color-scheme="light" or color-scheme="dark" does not depend on light-dark().
If you prefer one stylesheet request, import vue-syntax-highlighter/styles/complete.css instead. It contains the minified base followed by the minified fallback in the same @supports guard. It is larger than styles.css; do not import it together with the other two stylesheets.
Deferral needs IntersectionObserver, and virtualization measurement uses ResizeObserver when available. Both features degrade gracefully when the API is absent.
Async first paint
Built-in string engines are lazy. Without a preloaded engine object, the first render on the server and during hydration is plain text marked busy. If highlighted server HTML is required for SEO or a no-flash first paint, preload an engine object as documented in Server Rendering.
Virtualization constraints
Virtualization requires:
- a bounded
heightormaxHeight wrap=false- a fixed numeric
lineHeightfor deterministic math
Wrapped, variable-height lines are not virtualized. Unsupported combinations disable virtualization with a development warning. Line numbers, highlighted-line markers, and scroll behavior remain correct in virtualized mode.
Offscreen deferral
Deferral uses IntersectionObserver. When it is unavailable, the component starts immediately after mount. Deferral is inert during SSR. It renders plain text until the block approaches the viewport.
Prism language registry
refractor uses a process-global grammar registry. Prism engine instances share custom language registrations, including across SSR requests in the same process. The grammar data is stateless, so output is not corrupted, but registrations are not isolated per app/request and are not removed on dispose(). Use Shiki or highlight.js/lowlight if strict isolation is required.
Planned fixes
Known correctness issues and the additive-only release plan are tracked in the Roadmap. The roadmap describes intended changes; it is not a statement that a fix is already available in the current release.