Recipes
Practical registry-build patterns for docs sites, custom manifests, external data files, and framework-specific output.
Recipe 1: Docs-site registry
This is the common shape for a docs app that needs:
index.json- per-item component payloads
- a generated component index file
- theme and color outputs
import {
bannerExtension,
colorsExtension,
componentIndexExtension,
componentsExtension,
defineConfig,
indexBuildExtension,
validateExtension,
} from '@gentleduck/registry-build'
export default defineConfig({
sources: {
'registry:ui': {
path: '../../packages/registry-ui/src',
packageName: '@example/registry-ui',
referencePath: '/registry-ui/src',
},
'registry:example': {
path: '../../packages/registry-examples/src',
packageName: '@example/registry-examples',
referencePath: '/registry-examples/src',
indexStrategy: 'file',
},
},
registries: {
uis: [...],
examples: [...],
},
output: {
dir: '.',
},
targetPaths: {
'registry:ui': 'components/ui',
'registry:example': 'components',
},
extensions: [
bannerExtension({ name: 'My Docs' }),
validateExtension(),
indexBuildExtension(),
componentsExtension(),
componentIndexExtension({
framework: 'nextjs',
}),
colorsExtension(),
],
})
Recipe 2: Arch-style package repository
This pattern treats registry-build as a generic build core plus a custom extension. The core UI phases are disabled, and the custom extension emits repository metadata, package-file manifests, and search artifacts.
import { defineConfig } from '@gentleduck/registry-build'
import { archRepositoryExtension } from './arch-repository.extension'
export default defineConfig({
collections: {
packages: {
data: './data/packages.json',
metadata: {
repoOrder: ['core', 'extra'],
},
sources: {
pkgbuilds: {
glob: '**/PKGBUILD',
path: './pkgbuilds',
},
},
},
},
extensions: [
archRepositoryExtension({
collection: 'packages',
}),
],
output: {
dir: './dist',
},
})
This is the direction for non-UI consumers: collections plus extensions, not empty componentIndex or themes objects.
See the in-repo example in packages/registry-build/examples/arch-package-index.
If you want the full guided build, continue to Course and Arch Package Index.
Recipe 3: Custom manifest extension
If another tool needs a derived manifest after the registry build finishes, expose it through an afterBuild extension:
{
name: 'manifest',
stage: 'afterBuild',
async run(api) {
const manifest = {
outputs: api.listOutputs().map((output) => output.name),
itemCount: (api.getArtifact('index') as Array<unknown> | undefined)?.length ?? 0,
}
// write file, register output, set artifact
},
}
Use this pattern instead of patching the core runner.
Recipe 4: External static data files
Use root config file paths when colors/themes or registry entries live in static JSON files:
export default defineConfig({
registrySource: './data/registry-entries.json',
colors: {
data: './data/colors.json',
},
themes: {
data: './data/themes.json',
},
output: {
dir: '.',
},
})
This keeps static data portable and avoids importing giant objects into every consumer config file manually.
Recipe 5: Extend a base config
File-based composition:
export default defineConfig({
extends: ['./registry-build.base.ts', './registry-build.theme.ts'],
output: {
dir: '.',
},
})
Code-based composition:
import { defineConfig, mergeRegistryBuildConfigs } from '@gentleduck/registry-build'
import { base } from './registry-build.base'
import { theme } from './registry-build.theme'
export default defineConfig(mergeRegistryBuildConfigs(base, theme))
Use file-based composition when the config should stay mostly declarative. Use code-based composition when you need parameterized factories.
Recipe 6: Custom framework component index
If the built-in framework adapters are not enough, provide a custom generator:
componentIndexExtension({
generator(items) {
return [
'// custom generated file',
`export const items = ${JSON.stringify(items, null, 2)}`,
].join('\n')
},
})
That keeps the rest of the build intact while taking full control over the generated file.
Recipe 7: Fast local rebuild loop
Use split scripts so the raw CLI path remains available:
{
"scripts": {
"build:reg": "bun run build:reg:cli && bun run format:reg",
"build:reg:cli": "registry-build build",
"format:reg": "bunx biome format --write ./__ui_registry__/index.tsx"
}
}
Then for targeted local updates:
bun run build:reg:cli - --changed-only --changed ../../packages/registry-ui/src/button/button.tsx