This guide explains how to add new blocks and components to HextaUI.
Blocks are complex, feature-rich components organized by category (AI, Auth, Billing, Settings).
Create the component file in two locations:
-
Main component (used in the app):
components/blocks/{category}/{block-id}.tsxExample:
components/blocks/ai/ai-new-feature.tsx -
Registry component (for shadcn registry):
registry/new-york/blocks/{category}/{block-id}.tsxExample:
registry/new-york/blocks/ai/ai-new-feature.tsx
Note: Both files should contain the same component code. The registry file is used for the shadcn CLI, while the components file is used in the application.
Edit lib/blocks-registry.tsx:
-
Import the component at the top:
import AINewFeature from "@/components/blocks/ai/ai-new-feature";
-
Add to
blocksListarray (in the appropriate category section):{ id: "ai-new-feature", title: "New Feature", description: "Description of what this block does.", category: "ai", // or "auth", "billing", "settings" },
-
Add to
blockComponentsmapping:"ai-new-feature": AINewFeature,
Edit lib/block-examples.tsx:
Add a new case in the getBlockExampleProps function:
case "ai-new-feature": {
return {
// Add example props matching your component's interface
prop1: "value1",
prop2: 123,
onAction: () => {
/* example action */
},
};
}Edit lib/block-snippets.ts:
Add a new entry in the blockSnippets object:
"ai-new-feature": {
usageImports: `import AINewFeature from "@/components/blocks/ai/ai-new-feature";`,
usageCode: `<AINewFeature
prop1="value1"
prop2={123}
onAction={() => {
/* handle action */
}}
/>`,
},Edit registry.json:
Add a new entry in the items array (after the UI components, with other blocks):
{
"name": "ai-new-feature",
"type": "registry:component",
"title": "New Feature",
"description": "Description of what this block does.",
"registryDependencies": [
"button",
"card",
"input-group"
],
"files": [
{
"path": "registry/new-york/blocks/ai/ai-new-feature.tsx",
"type": "registry:component"
}
]
}Important:
- List all UI components used in
registryDependencies(e.g.,button,card,dialog, etc.) - Use the registry file path (
registry/new-york/blocks/...) - Verify dependencies match actual imports from
@/components/ui/
- Check that the block appears in
/blockspage - Check that the block page loads at
/blocks/ai-new-feature - Verify the demo works with example props
- Verify usage examples display correctly
Components are foundation UI elements (like Button, Card, Dialog, etc.).
Create the component in the registry:
registry/new-york/ui/{component-id}.tsx
Example: registry/new-york/ui/new-component.tsx
Edit lib/components-registry.tsx:
-
Import the demo component (if you have one):
import NewComponentDemo from "@/components/demo/new-component-demo";
-
Add to
componentsListarray:{ id: "new-component", title: "New Component", description: "Description of what this component does.", },
-
Add to
demoComponentsmapping (if you have a demo):"new-component": NewComponentDemo,
Edit lib/registry/snippets.ts:
Add a new entry in the componentSnippets object:
"new-component": {
usageImports: `import { NewComponent } from "@/components/ui/new-component"`,
usageCode: `<NewComponent>
Content here
</NewComponent>`,
demoCode: `"use client";
import { NewComponent } from "@/components/ui/new-component";
export function NewComponentDemo() {
return (
<NewComponent>
Demo content
</NewComponent>
);
}`,
},Edit registry.json:
Add a new entry in the items array (with other UI components):
{
"name": "new-component",
"type": "registry:ui",
"dependencies": ["@radix-ui/react-..."],
"registryDependencies": ["button"],
"files": [
{
"path": "registry/new-york/ui/new-component.tsx",
"type": "registry:ui"
}
]
}Important:
- Add external npm dependencies in
dependencies(e.g.,@radix-ui/react-dialog) - Add registry component dependencies in
registryDependencies(e.g.,button,card) - Only include dependencies that are actually imported and used
- Check that the component appears in
/componentspage - Check that the component page loads at
/components/new-component - Verify the demo works
- Verify usage examples display correctly
HextaUI/
├── components/
│ ├── blocks/ # Block components (used in app)
│ │ ├── ai/
│ │ ├── auth/
│ │ ├── billing/
│ │ └── settings/
│ └── ui/ # UI components (from registry)
├── registry/
│ └── new-york/
│ ├── blocks/ # Block registry files
│ │ ├── ai/
│ │ ├── auth/
│ │ ├── billing/
│ │ └── settings/
│ └── ui/ # Component registry files
├── lib/
│ ├── blocks-registry.tsx # Block metadata and mapping
│ ├── block-examples.tsx # Example props for blocks
│ ├── block-snippets.ts # Usage examples for blocks
│ ├── components-registry.tsx # Component metadata and mapping
│ └── registry/
│ └── snippets.ts # Usage examples for components
└── registry.json # Shadcn registry configuration
- Component file in
components/blocks/{category}/ - Registry file in
registry/new-york/blocks/{category}/ - Added to
blocksListinlib/blocks-registry.tsx - Added to
blockComponentsmapping - Added example props in
lib/block-examples.tsx - Added usage snippets in
lib/block-snippets.ts - Added entry to
registry.jsonwith correct dependencies
- Component file in
registry/new-york/ui/ - Added to
componentsListinlib/components-registry.tsx - Added demo component (if needed)
- Added usage snippets in
lib/registry/snippets.ts - Added entry to
registry.jsonwith correct dependencies
-
Naming Convention:
- Blocks:
{category}-{feature}(e.g.,ai-chat-history,auth-login-form) - Components:
{feature}(e.g.,button,card,dialog)
- Blocks:
-
Dependencies:
- Always verify
registryDependenciesmatch actual imports - Use the audit script to check for missing/extra dependencies
- Only include UI components from
@/components/ui/
- Always verify
-
Example Props:
- Make examples realistic and practical
- Include all required props
- Use proper TypeScript types
-
Usage Snippets:
- Keep snippets concise but complete
- Show the most common use case
- Include necessary imports
-
Testing:
- Always test the component/block after adding
- Verify the demo works
- Check that all pages load correctly
If you encounter issues:
- Check existing blocks/components for reference
- Verify all file paths are correct
- Ensure TypeScript types match
- Run the linter to catch errors
- Check the browser console for runtime errors