CLI Components
Codella provides production-ready components that you can scaffold into your React projects. Each component is fully customizable — you own the code.
Installation
Prerequisites
- Node.js 16+ installed
- An existing React project (or create one with
create-react-apporvite)
Install the CLI
# Using npm
npm install -D @codella-software/cli
# Using pnpm
pnpm add -D @codella-software/cli
# Using yarn
yarn add --dev @codella-software/cli
Install Core Dependencies
# Install the core utilities
npm install @codella-software/utils
# If using React components, also install React integration
npm install @codella-software/react
Using the CLI
# List available components
codella list
# Add a component to your project
codella add <component-name>
# Available components: form-builder, dynamic-table, private-outlet, table-filters, rich-content
PrivateOutlet
A React Router component that protects routes by checking authentication state and managing login redirects.
Features:
- ✅ Auth state checking before rendering protected routes
- ✅ Loading state display during authentication verification
- ✅ Automatic redirects for unauthenticated users
- ✅ Intended path preservation via localStorage
- ✅ Support for custom loader components
- ✅ Works with Firebase, Auth0, NextAuth, or any auth provider
Usage:
codella add private-outlet
import { PrivateOutlet } from '@/components/codella/private-outlet';
import { useAuth } from '@/hooks/useAuth';
import { Routes, Route } from 'react-router';
export function AppRoutes() {
const { user, isLoading } = useAuth();
return (
<Routes>
{/* Public route */}
<Route path="/login" element={<LoginPage />} />
{/* Protected routes */}
<Route
element={
<PrivateOutlet
isAuthenticated={!!user}
isLoading={isLoading}
loginPath="/login"
/>
}
>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Route>
</Routes>
);
}
DynamicTable
A full-featured table component with sorting, filtering, pagination, and row selection.
Features:
- ✅ Column sorting (ascending/descending)
- ✅ Column filtering with operators
- ✅ Pagination with customizable page size
- ✅ Row selection with checkboxes
- ✅ Empty and loading states
- ✅ Responsive design with Tailwind CSS
Usage:
codella add dynamic-table
import { DynamicTable } from './components/DynamicTable';
import { useTableService } from '@codella-software/react';
export function DataTable() {
const table = useTableService({
columns: [
{ key: 'id', label: 'ID' },
{ key: 'name', label: 'Name' },
],
data: [...],
sortable: true,
pagination: { pageSize: 10 },
});
return <DynamicTable {...table} />;
}
Form Builder
A production-ready, full-featured form system with 15 field types, multi-step forms, validation, grid layouts, modals, i18n support, and more. This is not a simple form helper — it's a complete form rendering engine that pairs with the FormBuilder class from @codella-software/utils.
codella add form-builder
What Gets Scaffolded
form-builder/
├── FormRenderer.tsx # Main form component
├── FieldRenderer.tsx # Field type router/dispatcher
├── FormModal.tsx # Accessible modal wrapper for forms
├── fields/ # 15 field components
│ ├── TextField.tsx # Text, email, password, url inputs
│ ├── NumberField.tsx # Number input with min/max/step
│ ├── TextareaField.tsx # Multi-line text input
│ ├── SelectField.tsx # Dropdown select with option groups
│ ├── AsyncSelectField.tsx # Async searchable select with debounce
│ ├── ComboboxField.tsx # Searchable combobox (single/multi, creatable)
│ ├── CheckboxField.tsx # Checkbox (single or multiple)
│ ├── RadioField.tsx # Radio button groups
│ ├── SwitchField.tsx # Toggle switch
│ ├── DateField.tsx # Date picker with min/max/disabled dates
│ ├── DateRangeField.tsx # Start/end date range picker
│ ├── TimeField.tsx # Time picker (12h/24h format)
│ ├── FileField.tsx # File upload with drag-and-drop
│ ├── ArrayField.tsx # Repeatable field groups (add/remove/reorder)
│ ├── RichContentField.tsx # Rich text editor integration
│ └── index.ts # Barrel exports
├── multi-step/ # Multi-step form support
│ ├── MultiStepFormRenderer.tsx # Multi-step orchestrator
│ ├── StepIndicator.tsx # Progress indicator
│ ├── StepNavigation.tsx # Next/Previous buttons
│ ├── StepContent.tsx # Step content wrapper
│ └── index.ts
└── __tests__/ # Comprehensive test suite
├── FormRenderer.test.tsx
├── FieldRenderer.test.tsx
├── FormModal.test.tsx
└── MultiStepComponents.test.tsx
Features
- ✅ 15 Built-in Field Types — text, number, textarea, select, async select, combobox, checkbox, radio, switch, date, date range, time, file upload, array (repeatable), rich content
- ✅ Multi-Step Forms — wizard-like forms with step indicator, navigation, and per-step validation
- ✅ Validation — Yup/Zod schema integration + custom validators
- ✅ Conditional Fields — show/hide fields based on form values
- ✅ Field Dependencies — cascade selections (e.g., country → state → city)
- ✅ Grid Layout — configurable multi-column layouts with
colSpan,rowSpan,colStart - ✅ Form Modal — accessible modal wrapper with focus trap, escape key, overlay click
- ✅ Custom Fields — register your own field components via
customFieldsmap - ✅ Array Fields — repeatable field groups with add/remove, reorder, min/max items, nested validation
- ✅ Async Select — server-side search with configurable debounce, preload, pagination
- ✅ i18n Ready — optional
ttranslation prop on all components withdefaultValuefallbacks - ✅ RxJS Events — subscribe to field changes, validation errors, submission lifecycle
- ✅ Accessible — ARIA attributes, keyboard navigation, focus management
- ✅ Dark Mode — automatic Tailwind dark mode support
- ✅ Fully Typed — complete TypeScript generics throughout
- ✅ Test Suite — unit tests included for all major components
Quick Start
import { FormRenderer } from '@/components/codella/form-builder/FormRenderer';
import { FormBuilder } from '@codella-software/utils';
import * as yup from 'yup';
interface ContactForm {
name: string;
email: string;
subject: string;
message: string;
}
export default function ContactPage() {
const builder = new FormBuilder<ContactForm>()
.addField({
name: 'name',
type: 'text',
label: 'Full Name',
required: true,
validation: yup.string().required('Name is required'),
})
.addField({
name: 'email',
type: 'email',
label: 'Email Address',
placeholder: 'you@example.com',
validation: yup.string().email('Invalid email').required('Email is required'),
})
.addField({
name: 'subject',
type: 'select',
label: 'Subject',
options: [
{ label: 'General Inquiry', value: 'general' },
{ label: 'Bug Report', value: 'bug' },
{ label: 'Feature Request', value: 'feature' },
],
})
.addField({
name: 'message',
type: 'textarea',
label: 'Message',
validation: yup.string().required().min(10, 'Please write at least 10 characters'),
})
.build();
return (
<FormRenderer
builder={builder}
showButtons
onSubmitSuccess={(data) => {
console.log('Submitted:', data);
}}
/>
);
}
Field Types Reference
| Type | Component | Description |
|---|---|---|
text | TextField | Text, email, password, url, tel inputs |
email | TextField | Email input with type="email" |
password | TextField | Password input with visibility toggle |
number | NumberField | Number input with min/max/step validation |
textarea | TextareaField | Multi-line text with auto-resize option |
select | SelectField | Dropdown with option groups, clearable |
asyncSelect | AsyncSelectField | Server-side search, debounce, preload, pagination |
combobox | ComboboxField | Searchable multi/single select, creatable options |
checkbox | CheckboxField | Single checkbox or checkbox group |
radio | RadioField | Radio button group |
switch | SwitchField | Toggle switch |
date | DateField | Date picker with min/max dates, disabled dates |
dateRange | DateRangeField | Start and end date selection |
time | TimeField | Time picker with 12h/24h format support |
file | FileField | File upload with drag-and-drop, preview |
array | ArrayField | Repeatable field groups with add/remove/reorder |
custom | Your component | Register custom field types via customFields |
Grid Layout
Control form layout with a multi-column grid system:
builder
.setLayout({
columns: 2, // 2-column grid
gap: '16px', // Gap between fields
rowGap: '24px', // Optional separate row gap
})
.addField({ name: 'firstName', type: 'text', label: 'First Name', colSpan: 1 })
.addField({ name: 'lastName', type: 'text', label: 'Last Name', colSpan: 1 })
.addField({ name: 'bio', type: 'textarea', label: 'Bio', colSpan: 2 }) // Full width
.build();
Array Fields
Create repeatable field groups (e.g., line items, addresses, team members):
builder
.addField({
name: 'teamMembers',
type: 'array',
label: 'Team Members',
fields: [
{ name: 'name', type: 'text', label: 'Name' },
{ name: 'role', type: 'select', label: 'Role', options: [
{ label: 'Developer', value: 'dev' },
{ label: 'Designer', value: 'design' },
]},
{ name: 'email', type: 'email', label: 'Email' },
],
minItems: 1,
maxItems: 10,
allowReorder: true,
addLabel: 'Add Team Member',
})
.build();
Array fields support:
- Add/remove items with min/max constraints
- Drag-to-reorder items (when
allowReorder: true) - Per-item nested validation via JSON-encoded error objects
- Custom default item values
- Custom "Add" button label
Async Select
Load options from an API with search, debounce, and pagination:
builder
.addField({
name: 'assignee',
type: 'asyncSelect',
label: 'Assign To',
loadOptions: async (search, formValues) => {
const res = await fetch(`/api/users?q=${search}`);
const users = await res.json();
return users.map(u => ({ label: u.name, value: u.id }));
},
typingPauseMs: 400, // Debounce delay (default: 300)
preload: true, // Load options on mount
loadOnOpen: true, // Reload when dropdown opens
maxRenderedOptions: 50, // Virtualize long lists
})
.build();
Conditional Fields
Show or hide fields based on other field values:
builder
.addField({ name: 'hasCompany', type: 'checkbox', label: 'I have a company' })
.addField({
name: 'companyName',
type: 'text',
label: 'Company Name',
condition: (values) => values.hasCompany === true,
})
.build();
Multi-Step Forms
Build wizard-style forms with validation per step:
import { MultiStepFormRenderer } from '@/components/codella/form-builder/multi-step';
builder
.addStep({ title: 'Personal Information' })
.addField({ name: 'firstName', type: 'text', label: 'First Name', step: 0 })
.addField({ name: 'lastName', type: 'text', label: 'Last Name', step: 0 })
.addStep({ title: 'Address' })
.addField({ name: 'street', type: 'text', label: 'Street', step: 1 })
.addField({ name: 'city', type: 'text', label: 'City', step: 1 })
.addStep({ title: 'Review' })
.build();
<MultiStepFormRenderer
builder={builder}
onComplete={(data) => console.log('Done:', data)}
/>
The multi-step form includes:
- StepIndicator — visual progress bar showing current/completed/upcoming steps
- StepNavigation — Next/Previous buttons with per-step validation
- StepContent — animated step transitions
Form Modal
Wrap any form in an accessible modal dialog:
import { FormModal } from '@/components/codella/form-builder/FormModal';
<FormModal
title="Edit Profile"
description="Update your account information"
onClose={() => setOpen(false)}
width="600px"
closeOnEscape
closeOnOverlayClick
>
<FormRenderer builder={builder} showButtons />
</FormModal>
Features: focus trap, escape key handling, overlay click, scroll lock, accessible ARIA attributes.
Custom Fields
Register your own field types:
function ColorPickerField({ value, onChange, config }) {
return (
<div>
<label>{config.label}</label>
<input type="color" value={value} onChange={(e) => onChange(e.target.value)} />
</div>
);
}
const customFields = new Map([['colorPicker', ColorPickerField]]);
<FormRenderer builder={builder} customFields={customFields} />
Form Buttons
Customize submit, reset, and cancel buttons:
builder
.setButtons({
submit: { label: 'Save Changes', variant: 'default', fullWidth: true },
reset: { label: 'Reset Form', show: true, variant: 'outline' },
cancel: { label: 'Discard', show: true, onClick: () => router.back() },
position: 'right', // 'left' | 'center' | 'right' | 'apart'
})
.build();
Internationalization (i18n)
All form components accept an optional t translation function, compatible with react-i18next or any i18n library:
import { useTranslation } from 'react-i18next';
function MyForm() {
const { t } = useTranslation();
const builder = /* ... */;
return <FormRenderer builder={builder} t={t} />;
}
The t prop flows through to all field components. Every hardcoded string has a translation key with a defaultValue fallback, so forms work without any i18n library but are trivially localizable:
| Translation Key | Default Value | Used In |
|---|---|---|
form.submit | Submit | FormRenderer |
form.reset | Reset | FormRenderer |
form.cancel | Cancel | FormRenderer |
form.close | Close | FormModal |
form.selectOption | Select an option | SelectField, ComboboxField |
form.search | Search... | ComboboxField, AsyncSelectField |
form.create | Create "..." | ComboboxField |
form.noResults | No results found | ComboboxField, AsyncSelectField |
form.pickDate | Pick a date | DateField |
form.clearDate | Clear date | DateField |
form.startDate | Start Date | DateRangeField |
form.endDate | End Date | DateRangeField |
form.selectDateRange | Select date range | DateRangeField |
form.clear | Clear | DateRangeField, TimeField |
form.selectTime | Select time | TimeField |
form.hour | Hour | TimeField |
form.minute | Minute | TimeField |
form.period | Period | TimeField |
form.am | AM | TimeField |
form.pm | PM | TimeField |
form.addItem | Add item | ArrayField |
form.moveUp | Move up | ArrayField |
form.moveDown | Move down | ArrayField |
form.removeItem | Remove item N | ArrayField |
form.loading | Loading... | AsyncSelectField |
form.noOptions | No options | AsyncSelectField |
form.andMore | And N more... | AsyncSelectField |
Events System
Subscribe to form lifecycle events via RxJS:
useEffect(() => {
const sub = builder.events$.subscribe((event) => {
switch (event.type) {
case 'submitSuccess':
toast.success('Form saved!');
break;
case 'submitError':
toast.error(event.payload.error.message);
break;
case 'fieldChange':
console.log(`${event.payload.fieldName} = ${event.payload.value}`);
break;
}
});
return () => sub.unsubscribe();
}, [builder]);
TableFilters
An advanced filtering UI for tables with sort controls.
Features:
- ✅ Multiple filter types (text, date, number, select)
- ✅ Filter operators
- ✅ Sort direction toggle
- ✅ Active filter display
- ✅ Clear filters button
Usage:
codella add table-filters
import { TableFilters } from './components/TableFilters';
export function FilterPage() {
const table = useTableService({ ... });
return (
<>
<TableFilters {...table} />
<DynamicTable {...table} />
</>
);
}
RichContent
A full-featured rich text editor with formatting toolbar, undo/redo, support for headings, lists, images, and more. Includes both editor and form field integration components.
Features:
- ✅ Text formatting (bold, italic, underline, strikethrough, code)
- ✅ Block types (paragraphs, headings, lists, images, blockquotes, code blocks)
- ✅ Undo/Redo with configurable history
- ✅ Image upload support
- ✅ FormBuilder field integration (CustomFieldProps compatible)
- ✅ HTML/JSON/PlainText serialization
- ✅ Keyboard shortcuts support
- ✅ Extensible middleware hooks
- ✅ Available in base and shadcn/ui variants
Usage:
codella add rich-content
The scaffolding creates:
rich-content/rich-content.tsx- Parent component with singleuseRichContenthook, composes toolbar + editorrich-content/rich-content-toolbar.tsx- Formatting toolbar with mark toggles, headings, lists, undo/redorich-content/rich-content-editor.tsx- Styled contentEditable div (receives ref and focus callbacks from parent)form-builder/fields/RichContentField.tsx- CustomFieldProps-compatible form field with HTML serialization
Basic Editor Example:
import { RichContent } from '@/components/codella/rich-content';
export function MyEditor() {
return (
<div>
<h1>Write Your Post</h1>
<RichContent />
</div>
);
}
The RichContent component manages its own editorRef and useRichContent hook internally — just render it and it works out of the box.
Form Field Example:
import { FormRenderer } from '@/components/codella/form-builder/FormRenderer';
import { FormBuilder } from '@codella-software/utils';
import { RichContentField } from '@/components/codella/form-builder/fields';
const customFields = new Map([['richContent', RichContentField]]);
const builder = new FormBuilder()
.addField({
name: 'title',
type: 'text',
label: 'Title',
})
.addField({
name: 'content',
type: 'custom',
label: 'Content',
customType: 'richContent', // Maps to RichContentField via customFields
})
.build();
<FormRenderer builder={builder} customFields={customFields} />
Custom Editor with useRichContent:
For full control over configuration, use the useRichContent hook directly:
import { useRichContent } from '@codella-software/react';
import { useRef } from 'react';
export function ConfiguredEditor() {
const editorRef = useRef<HTMLDivElement>(null);
const {
toggleMark, insertHeading, undo, redo,
canUndo, canRedo, isFocused, setFocus,
} = useRichContent({
editorRef: editorRef as any,
maxHistorySteps: 100,
allowedMarks: ['bold', 'italic', 'underline'],
imageUploadHandler: async (file) => {
const formData = new FormData();
formData.append('file', file);
const res = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const data = await res.json();
return { url: data.url };
},
});
return (
<div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
<button onClick={() => toggleMark('bold')}>Bold</button>
<button onClick={() => toggleMark('italic')}>Italic</button>
<button onClick={() => insertHeading(2)}>H2</button>
<button onClick={() => undo()} disabled={!canUndo}>Undo</button>
<button onClick={() => redo()} disabled={!canRedo}>Redo</button>
</div>
<div
ref={editorRef}
contentEditable
suppressContentEditableWarning
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
style={{
border: isFocused ? '2px solid #3b82f6' : '1px solid #ccc',
padding: '12px',
minHeight: '200px',
borderRadius: '4px',
outline: 'none',
}}
/>
</div>
);
}
Variants:
The CLI offers two design variants:
Base Variant - Minimal styling with lucide icons
codella add rich-content --variant base
Shadcn/ui Variant - Modern design with shadcn/ui components
codella add rich-content --variant shadcn
See the Rich Content Editor documentation for complete API reference, middleware hooks, serializers, and advanced examples.
Configuration
The CLI scaffolds components based on your codella.config.json file. This configuration controls component generation and output.
Creating Configuration
First, initialize your project with:
codella init
This creates a codella.config.json file in your project root:
{
"ui": "shadcn",
"typescript": true,
"outputDir": "@/components/codella",
"aliases": {
"@": "./src"
},
"theme": {
"primary": "#3b82f6",
"secondary": "#6b7280"
}
}
Configuration Options
ui - UI Framework
Determines which component variant is scaffolded.
Supported values:
"shadcn"- shadcn/ui styled components (default)"mui"- Material-UI components"antd"- Ant Design components"base"- Plain HTML with Tailwind CSS
{
"ui": "shadcn"
}
typescript - TypeScript Support
Generates .tsx files with full type safety or .jsx files.
{
"typescript": true
}
outputDir - Component Output Directory
Where scaffolded components are placed in your project.
{
"outputDir": "@/components/codella"
}
Use path aliases for cleaner imports, or specify relative paths:
{
"outputDir": "src/features/ui-components"
}
aliases - Path Aliases
Maps import aliases used in your project (from tsconfig.json or custom).
{
"aliases": {
"@": "./src",
"components": "./src/components",
"utils": "./src/lib/utils",
"hooks": "./src/hooks"
}
}
These aliases control how components import dependencies. For example, with the above aliases, a component might import:
import { useTableService } from '@codella-software/react'
import { DynamicTable } from '@/components/codella/dynamic-table'
import { CustomButton } from '@/components/ui/button'
import { formatDate } from '@/utils/date'
theme (optional) - Tailwind Colors
Tailwind CSS theme colors for component styling. Auto-extracted from your tailwind.config.js during initialization.
{
"theme": {
"primary": "#3b82f6",
"secondary": "#6b7280",
"accent": "#a855f7",
"background": "#ffffff",
"foreground": "#000000",
"muted": "#e5e7eb",
"muted-foreground": "#6b7280"
}
}
Components use these colors for styling. Update the theme to match your brand colors.
Modifying Configuration
After creating codella.config.json, you can edit it manually:
Change the UI framework:
{
"ui": "mui"
}
Add new aliases:
{
"aliases": {
"types": "@/types",
"constants": "@/constants"
}
}
Update output directory:
{
"outputDir": "@/features/ui"
}
Update theme colors:
{
"theme": {
"primary": "#ff0000",
"secondary": "#00ff00"
}
}
After editing, new components scaffolded with codella add will use the updated configuration.
Configuration Examples
Next.js with shadcn/ui:
{
"ui": "shadcn",
"typescript": true,
"outputDir": "@/components/codella",
"aliases": {
"@/*": "./*"
}
}
Vite + React with Material-UI:
{
"ui": "mui",
"typescript": true,
"outputDir": "src/components/codella",
"aliases": {
"@": "./src"
}
}
Create React App with Ant Design:
{
"ui": "antd",
"typescript": true,
"outputDir": "src/components",
"aliases": {
"@": "./src"
}
}
Auto-Detection
During codella init, the CLI automatically detects:
- UI Framework - Checks
package.jsonfor framework dependencies - TypeScript - Looks for
tsconfig.json - Path Aliases - Reads from
tsconfig.jsonorjsconfig.json - Theme Colors - Extracts from
tailwind.config.jsif available - Output Directory - Uses sensible defaults based on detected aliases
To use auto-detected values without prompts:
codella init --yes
To customize, use interactive mode:
codella init
Customization
All components are single-file, fully customizable. You own the code completely.
To customize, simply edit the component file in your output directory (configured in codella.config.json) and make your changes. Use Tailwind CSS classes or add your own styles as needed.