The official shadcn/ui-style code distribution system for Fict — a reactive UI library with compiler-driven fine-grained reactivity.
Instead of installing a component library as a dependency, @fictjs/shadcn copies beautifully-designed, accessible component source code directly into your project. You own the code, you customize it, and the CLI helps you keep it up to date.
fictcnCLI — scaffold, add, remove, diff, update, and validate UI components from the terminal- 206 built-in registry entries — core Fict-native set plus expanded compatibility catalog
- Core Fict-native set — 35 UI components + 8 blocks + 5 themes
- Flexible registry source — use the bundled offline registry or a remote JSON registry
- Deterministic lock file —
fictcn.lock.jsontracks installed versions and SHA-256 file hashes for drift detection - Automatic dependency resolution — registry dependencies are resolved with circular dependency detection
- Tailwind CSS + CSS variables — baseline configuration generated for you, including PostCSS and
tailwindcss-animate - Package manager agnostic — auto-detects pnpm, npm, yarn, or bun
pnpm add -D @fictjs/shadcn
# or
npm install -D @fictjs/shadcn
# or
yarn add -D @fictjs/shadcn
# or
bun add -D @fictjs/shadcnPrerequisites: Node.js 18+ and a Fict project. Tailwind baseline can be bootstrapped by
fictcn init.
If
@fictjs/shadcnis installed locally, run the CLI via your package manager:pnpm exec fictcn,npm exec fictcn,yarn fictcn, orbunx fictcn.
# 1. Initialize your project (generates config, globals.css, tailwind config, utilities)
pnpm fictcn init
# 2. Add components — dependencies are resolved automatically
pnpm fictcn add button dialog tabs table
# 3. Import and use in your Fict componentsimport { Button } from '@/components/ui/button'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
function App() {
let count = $state(0)
return (
<Card>
<CardHeader>
<CardTitle>Counter</CardTitle>
</CardHeader>
<CardContent>
<Button onClick={() => count++}>Clicked {count} times</Button>
</CardContent>
</Card>
)
}Scaffolds the project baseline: fictcn.json config, globals.css with design tokens, Tailwind config (e.g. tailwind.config.ts / tailwind.config.js / tailwind.config.mjs / tailwind.config.cjs), PostCSS config, and utility files (cn.ts, variants.ts). Existing scaffold files are preserved by default; pass --force to overwrite. Installs required dependencies unless --skip-install is passed.
fictcn init [--force] [--skip-install] [--dry-run]Adds one or more components to your project from the configured registry (builtin or remote URL). Registry dependencies (e.g., dialog depends on button) are resolved and installed automatically.
fictcn add <components...> [--overwrite] [--skip-install] [--dry-run]
# Examples
fictcn add button
fictcn add dialog tabs accordion tooltip
fictcn add select --overwrite # replace existing filesInstall pre-built UI blocks — higher-level compositions of multiple components.
fictcn blocks add <blocks...> [--overwrite] [--skip-install] [--dry-run]
fictcn blocks list
# Examples
fictcn blocks add auth/login-form dashboard/layoutApply color themes that override CSS custom properties with light/dark variants.
fictcn theme apply <themes...> [--overwrite] [--dry-run]
fictcn theme list
# Examples
fictcn theme apply theme-slateShow a unified diff of local modifications against the configured registry source. Useful for reviewing what you've customized before updating.
fictcn diff [entries...]
# Examples
fictcn diff # diff all installed entries
fictcn diff button card # diff specific componentsUpdate installed registry entries from the configured registry source. Without --force, entries with local modifications are skipped.
fictcn update [entries...] [--force] [--skip-install] [--dry-run]
# Examples
fictcn update # update all
fictcn update button card # update specific entries
fictcn update --force # overwrite local changesRemove installed entries and tracked files from your project and lock file. By default, edited files are protected; pass --force to remove anyway.
fictcn remove <entries...> [--force] [--dry-run]
# Examples
fictcn remove button
fictcn remove auth/login-form theme-slate
fictcn remove button --forceValidate that your project is correctly configured: checks for fictcn.json, tsconfig.json alias paths, globals.css, your configured Tailwind file (tailwindConfig), and required npm dependencies.
fictcn doctorList all available registry entries.
fictcn list [--type components|blocks|themes|all] [--json] [--registry builtin|<url>]When --registry is omitted, the CLI uses fictcn.json (registry field) if available.
Search registry entries by keyword.
fictcn search <query> [--registry builtin|<url>]
# Examples
fictcn search dialog
fictcn search formRunning fictcn init creates a fictcn.json at your project root:
| Field | Description | Default |
|---|---|---|
componentsDir |
Project-relative directory where UI component files are written | src/components/ui |
libDir |
Project-relative directory for utility files (cn.ts, variants.ts) |
src/lib |
css |
Project-relative path to the global CSS file with design tokens | src/styles/globals.css |
tailwindConfig |
Project-relative path to the Tailwind CSS configuration file | tailwind.config.ts |
registry |
Registry source (builtin, http(s)://..., or file://...) |
builtin |
aliases.base |
TypeScript path alias prefix for imports | @ |
For remote registries, each entry in index.json (or registry.json) should include:
name, type, version, dependencies, registryDependencies, and files.
files supports two formats:
- Inline content:
[{ path, content }] - File reference:
[{ path }](the CLI fetches file contents relative to the registry JSON URL)
Remote registry safety rules:
http(s)registries can only referencehttp(s)template file URLs.http(s)registries only allow same-origin template file URLs by default.file://registries can only referencefile://template files within the same registry root directory.- Generated template output paths must stay project-relative and cannot traverse parent directories.
@fictjs/shadcn is compatible with shadcn-style registry type tags:
registry:ui,registry:block,registry:styleregistry:hook,registry:lib(treated as installable component entries)
Remote registry requests include:
- retry on transient failures (default
2retries) - request timeout (default
10000ms) - short-lived in-process cache (default
10000ms) - concurrent file fetches for file-reference registries (default
16)
Optional environment variables:
FICTCN_REGISTRY_RETRIESFICTCN_REGISTRY_TIMEOUT_MSFICTCN_REGISTRY_RETRY_DELAY_MSFICTCN_REGISTRY_CACHE_TTL_MSFICTCN_REGISTRY_FETCH_CONCURRENCYFICTCN_ALLOW_CROSS_ORIGIN_REGISTRY_FILES(1to allow cross-originhttp(s)template file URLs)
fictcn.lock.json is generated automatically and tracks every installed entry with:
- Version — the registry version at install time
- Timestamp — when the entry was installed
- File hashes — SHA-256 hashes of every generated file, enabling drift detection via
fictcn diff
Commit this file to version control so your team stays in sync.
The bundled registry now includes 206 entries in total:
- Core Fict-native entries: 35 components + 8 blocks + 5 themes
- Expanded Fict-native entries: 25 compatibility components + 132 blocks + 1 theme bootstrap template
Expanded entries are now generated as usable Fict templates (with meaningful imports, dependencies, and starter UI), rather than empty placeholder shells.
| Category | Components |
|---|---|
| Base | button, badge, card, separator, avatar, aspect-ratio, skeleton, label, input, textarea |
| Forms | checkbox, radio-group, switch, select, combobox, slider, toggle, toggle-group, form |
| Overlay | dialog, alert-dialog, popover, tooltip, hover-card, sheet |
| Navigation | dropdown-menu, context-menu, menubar, tabs, accordion, collapsible, navigation-menu |
| Feedback | toast, progress |
| Data | table |
| Block | Description |
|---|---|
auth/login-form |
Email/password login card |
auth/signup-form |
Account creation form |
settings/profile |
User profile settings page |
dashboard/layout |
Dashboard shell with header |
dashboard/sidebar |
Collapsible sidebar navigation |
tables/users-table |
Data table with user records |
dialogs/confirm-delete |
Destructive action confirmation modal |
forms/validated-form |
Form with field-level validation |
| Theme | Description |
|---|---|
theme-default |
Neutral gray palette |
theme-slate |
Cool slate tones |
theme-zinc |
Zinc/charcoal palette |
theme-stone |
Warm stone tones |
theme-brand-ocean |
Blue ocean brand palette |
All themes provide light and dark mode variants via CSS custom properties and can be applied with a class name (e.g., class="theme-slate dark").
your-project/
├── fictcn.json # CLI configuration
├── fictcn.lock.json # Deterministic lock file
├── tailwind.config.ts # Auto-configured with design tokens
├── postcss.config.mjs # PostCSS with Tailwind + Autoprefixer
└── src/
├── components/
│ └── ui/
│ ├── button.tsx # ← added by `fictcn add button`
│ ├── card.tsx
│ └── ...
├── lib/
│ ├── cn.ts # clsx + tailwind-merge utility
│ └── variants.ts # class-variance-authority re-export
└── styles/
└── globals.css # Tailwind directives + design tokens
-
fictcn initdetects your project root, package manager, and TypeScript config. It generates baseline files and installs core dependencies (@fictjs/ui-primitives,class-variance-authority,clsx,tailwind-merge). -
fictcn add buttonlooks upbuttonin your configured registry, resolves any registry dependencies (e.g., addingdialogalso pulls inbutton), renders template files with your configured paths and aliases, writes them tocomponentsDir, and records everything in the lock file with SHA-256 hashes. -
You own the code. Modify the generated components however you like. When the registry updates, use
fictcn diffto see what changed, andfictcn updateto pull in upstream improvements — your local changes are preserved unless you pass--force. -
fictcn doctorvalidates the full setup: config file, TypeScript aliases, CSS tokens, Tailwind config, and npm dependencies — catching misconfigurations early.
All CLI commands are also available as TypeScript functions:
import { runInit, runAdd, runRemove, runDiff, runDoctor, runList } from '@fictjs/shadcn'
// Initialize programmatically
await runInit({ skipInstall: true })
// Add components
await runAdd({ components: ['button', 'dialog'], overwrite: false })
// Remove entries
await runRemove({ entries: ['dialog'] })
// Check for drift
const { patches } = await runDiff()
// Validate project health
const { ok, issues } = await runDoctor()
// List registry entries
const output = runList({ type: 'components', json: true })This repo includes a Fict-native Storybook setup for developing and previewing components:
pnpm storybook # start dev server on port 6006
pnpm storybook:build # build static site
pnpm storybook:smoke # CI smoke test# Install dependencies
pnpm install --ignore-workspace
# Development
pnpm dev # watch mode
pnpm build # production build
# Quality
pnpm lint # ESLint
pnpm typecheck # TypeScript validation
pnpm test # run all tests
pnpm test:watch # watch modeSchemas for editor autocompletion and validation:
- Config:
https://fict.js.org/schemas/fictcn.schema.json - Lock file:
https://fict.js.org/schemas/fictcn-lock.schema.json
Local copies are in the schemas/ directory.
MIT © Fict
{ "$schema": "https://fict.js.org/schemas/fictcn.schema.json", "version": 1, "style": "tailwind-css-vars", "componentsDir": "src/components/ui", "libDir": "src/lib", "css": "src/styles/globals.css", "tailwindConfig": "tailwind.config.ts", "registry": "builtin", "aliases": { "base": "@", }, }