Output
Understand the files that hollows-ui generates: the .hollows.json manifest, the registry.js auto-loader, and the illustration assets.
Directory structure
After running npx hollows-ui build, the following files are generated in your configured output directory (default: src/hollows/):
rc/hollows/ .hollows.json # All empty state definitions registry.js # Runtime auto-loader (import once) registry.d.ts # TypeScript declarations illustrations/ inbox-empty.svg # SVG illustration per hollow search-empty.svg upload-empty.svg dashboard-empty.svgThese files should be committed to version control. They are deterministic — running the build again with the same source produces identical output.
.hollows.json
The central manifest file containing every detected hollow and its generated empty state definition. This is the source of truth that the runtime adapter reads from.
1{2 "version": 1,3 "generatedAt": "2026-04-08T12:00:00.000Z",4 "theme": {5 "mode": "dark",6 "accentColor": "#f0a030",7 "borderRadius": 12,8 "fontFamily": "inherit"9 },10 "hollows": [11 {12 "name": "user-inbox",13 "category": "list",14 "source": {15 "filePath": "src/components/inbox.tsx",16 "line": 6,17 "emptyExpr": "messages.length === 0"18 },19 "copy": {20 "headline": "No messages yet",21 "description": "When you receive messages, they'll appear here.",22 "cta": {23 "label": "Compose a message",24 "action": "primary"25 }26 },27 "illustration": {28 "src": "./illustrations/inbox-empty.svg",29 "alt": "Empty inbox illustration",30 "width": 180,31 "height": 16032 },33 "layout": {34 "align": "center",35 "padding": "48px 24px",36 "maxWidth": "360px",37 "gap": "16px"38 },39 "confidence": 0.94,40 "hash": "a1b2c3d4"41 },42 {43 "name": "search-results",44 "category": "search",45 "source": {46 "filePath": "src/components/search.tsx",47 "line": 14,48 "emptyExpr": "results.length === 0"49 },50 "copy": {51 "headline": "No results found",52 "description": "Try adjusting your search terms or filters.",53 "cta": {54 "label": "Clear filters",55 "action": "secondary"56 }57 },58 "illustration": {59 "src": "./illustrations/search-empty.svg",60 "alt": "No search results illustration",61 "width": 160,62 "height": 14063 },64 "layout": {65 "align": "center",66 "padding": "40px 24px",67 "maxWidth": "340px",68 "gap": "12px"69 },70 "confidence": 0.89,71 "hash": "e5f6g7h8"72 }73 ]74}Field reference
| Field | Type | Description |
|---|---|---|
| version | number | Schema version for forward compatibility |
| generatedAt | string | ISO 8601 timestamp of the build |
| theme | object | The theme settings used during generation |
| name | string | Unique identifier matching the name prop on <Hollow> |
| category | string | The auto-detected or manually set classification |
| source | object | Source location of the <Hollow> component for traceability |
| copy | object | Generated text content: headline, description, and CTA |
| illustration | object | Path, alt text, and dimensions for the SVG illustration |
| layout | object | Computed CSS layout properties for the empty state container |
| confidence | number | Classification confidence score (0-1) |
| hash | string | Content hash for cache invalidation |
registry.js
The registry file is a side-effect module that loads the manifest and registers each hollow definition with the runtime store. You import it once in your app entry point.
1// Auto-generated by hollows-ui — do not edit2import { registerHollows } from 'hollows-ui/runtime'3import manifest from './.hollows.json'45// Import illustration assets so bundlers can process them6import inboxEmpty from './illustrations/inbox-empty.svg'7import searchEmpty from './illustrations/search-empty.svg'8import uploadEmpty from './illustrations/upload-empty.svg'9import dashboardEmpty from './illustrations/dashboard-empty.svg'1011const illustrations = {12 'inbox-empty': inboxEmpty,13 'search-empty': searchEmpty,14 'upload-empty': uploadEmpty,15 'dashboard-empty': dashboardEmpty,16}1718registerHollows(manifest, illustrations)The registerHollows function stores the definitions in an in-memory map. When a <Hollow> component mounts, it looks up its definition by name and renders the empty state when the empty condition is true.
// Import once at the app rootimport './hollows/registry'export default function RootLayout({ children }) { return ( <html lang="en"> <body>{children}</body> </html> )}Illustrations
Each hollow gets an SVG illustration matched to its category. The illustrations are generated with the accent color from your theme config baked in, so they blend naturally with your design system.
You can replace any generated illustration with your own SVG. Just keep the same filename and dimensions. The registry will pick up your custom file on the next build.
Versioning
The hash field on each hollow entry is computed from the source component's AST and the theme config. When you re-run the build, hollows only regenerates entries whose hash has changed. This makes incremental builds fast — typically under 500ms for unchanged projects.
hollows-ui v1.0.0 Scanning... Found 4 hollows in 12 files Checking hashes... user-inbox → unchanged (skipped) search-results → unchanged (skipped) file-gallery → changed (rebuilding) analytics-panel → unchanged (skipped) Regenerating 1 hollow... Done in 0.4s