Performance
Hollows UI is designed to add minimal overhead to your application.
Bundle Size
The Hollows UI runtime is lightweight. The core library and framework adapters are designed to minimize your bundle footprint.
~2.1 kB
Core runtime (gzipped)
~0.8 kB
React adapter (gzipped)
~0.3 kB
Per illustration (avg)
The generated registry JSON is typically under 1 kB for 10 components. Illustrations are the largest part of the payload, but can be lazy-loaded to avoid impacting initial page load.
Tree-shaking
Hollows UI is fully tree-shakeable. Only the components and illustrations you actually use end up in your production bundle.
// Only the Hollow component and the 'empty-inbox' illustration// are included in the bundleimport { Hollow } from 'hollows-ui/react'<Hollow name="user-inbox" empty={!data}> <InboxList items={data} /></Hollow>The CLI generates a registry that only references the illustrations your app actually needs. Unused illustrations from the built-in set are never bundled.
What gets tree-shaken
- ✓Unused illustrations (SVG code never enters the bundle)
- ✓Unused framework adapters (only your framework is included)
- ✓Unused themes (only the active theme ships)
- ✓Unused locale data (only configured locales are bundled)
Lazy Illustration Loading
By default, illustrations are inlined as SVG strings in the registry. For applications with many empty states, enable lazy loading to defer illustration fetching until the empty state is actually displayed.
import { defineConfig } from 'hollows-ui'export default defineConfig({ performance: { lazyIllustrations: true, },})When enabled, the build step outputs illustrations as separate files in src/hollows/illustrations/ and the registry references them by path. The runtime loads them on demand using dynamic imports.
// With lazy loading enabled, illustrations are code-split// The SVG is only fetched when the empty state renders<Hollow name="user-inbox" empty={!data}> <InboxList items={data} /></Hollow>// ^ If data exists, the illustration is never loadedTip: Lazy loading adds a brief flash when the empty state first appears. Set preload: true on critical empty states to prefetch their illustration during idle time.
Incremental Builds
The CLI tracks which components have changed since the last build. On subsequent runs, only modified or new components are re-processed.
# First build — processes all componentsnpx hollows-ui build# Built 12 empty states in 340ms# After editing one componentnpx hollows-ui build# Built 1 empty state in 45ms (11 cached)The cache is stored in node_modules/.hollows-cache/. To force a full rebuild, use the --force flag.
# Force full rebuildnpx hollows-ui build --force# Watch mode — rebuilds on file changesnpx hollows-ui build --watchRuntime Performance
At runtime, the Hollow component is essentially a conditional renderer. When data is present, it renders your children with zero overhead. When empty, it renders a static SVG and text -- no animation libraries, no heavy DOM operations.
Performance characteristics
| Metric | Value |
|---|---|
| Initial render (empty state) | < 1ms |
| Re-render on data change | < 0.5ms |
| DOM nodes (empty state) | 8-15 nodes |
| Memory overhead per instance | < 2 KB |
| CSS-only responsive behavior | No JS resize listeners |
Measuring Impact
Use the built-in analyze command to see a breakdown of what Hollows adds to your bundle.
npx hollows-ui analyze# Output:# Bundle Analysis# ─────────────────────────────────# Core runtime: 2.1 kB (gzip)# React adapter: 0.8 kB (gzip)# Registry (12 items): 0.9 kB (gzip)# Illustrations (8): 2.4 kB (gzip)# Theme (minimal): 0.3 kB (gzip)# ─────────────────────────────────# Total: 6.5 kB (gzip)