Skip to main content

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-app or vite)

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 customFields map
  • 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 t translation prop on all components with defaultValue fallbacks
  • 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

TypeComponentDescription
textTextFieldText, email, password, url, tel inputs
emailTextFieldEmail input with type="email"
passwordTextFieldPassword input with visibility toggle
numberNumberFieldNumber input with min/max/step validation
textareaTextareaFieldMulti-line text with auto-resize option
selectSelectFieldDropdown with option groups, clearable
asyncSelectAsyncSelectFieldServer-side search, debounce, preload, pagination
comboboxComboboxFieldSearchable multi/single select, creatable options
checkboxCheckboxFieldSingle checkbox or checkbox group
radioRadioFieldRadio button group
switchSwitchFieldToggle switch
dateDateFieldDate picker with min/max dates, disabled dates
dateRangeDateRangeFieldStart and end date selection
timeTimeFieldTime picker with 12h/24h format support
fileFileFieldFile upload with drag-and-drop, preview
arrayArrayFieldRepeatable field groups with add/remove/reorder
customYour componentRegister 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 KeyDefault ValueUsed In
form.submitSubmitFormRenderer
form.resetResetFormRenderer
form.cancelCancelFormRenderer
form.closeCloseFormModal
form.selectOptionSelect an optionSelectField, ComboboxField
form.searchSearch...ComboboxField, AsyncSelectField
form.createCreate "..."ComboboxField
form.noResultsNo results foundComboboxField, AsyncSelectField
form.pickDatePick a dateDateField
form.clearDateClear dateDateField
form.startDateStart DateDateRangeField
form.endDateEnd DateDateRangeField
form.selectDateRangeSelect date rangeDateRangeField
form.clearClearDateRangeField, TimeField
form.selectTimeSelect timeTimeField
form.hourHourTimeField
form.minuteMinuteTimeField
form.periodPeriodTimeField
form.amAMTimeField
form.pmPMTimeField
form.addItemAdd itemArrayField
form.moveUpMove upArrayField
form.moveDownMove downArrayField
form.removeItemRemove item NArrayField
form.loadingLoading...AsyncSelectField
form.noOptionsNo optionsAsyncSelectField
form.andMoreAnd 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 single useRichContent hook, composes toolbar + editor
  • rich-content/rich-content-toolbar.tsx - Formatting toolbar with mark toggles, headings, lists, undo/redo
  • rich-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:

  1. UI Framework - Checks package.json for framework dependencies
  2. TypeScript - Looks for tsconfig.json
  3. Path Aliases - Reads from tsconfig.json or jsconfig.json
  4. Theme Colors - Extracts from tailwind.config.js if available
  5. 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.