Skip to main content

Introduction

TypeScript-first · React · Framework-agnostic core

Build complex UI,
without the complexity

Type-safe builders and reactive services for forms, tables, filters, tabs, and live updates. Framework-agnostic core with thin React hooks on top.

$ npm install @codella-software/utils @codella-software/react

Two packages

@codella-software/utilsFramework-agnostic builders and services — zero React dependency
@codella-software/reactReact hooks and providers for every core service

What you get

🧱
FormBuilder

Type-safe form configuration with Yup validation, field dependencies, multi-step flows, and submit middleware.

📊
TableBuilder

Typed column configs with pagination, sorting, row selection, and expandable rows. Works with any table renderer.

🔍
FiltersAndSort

Reactive filter, sort, and pagination state — persisted to storage with debounce support.

📑
TabsService

Tab state management with routing integration and notification badge support.

✏️
RichContent

Pluggable rich text editing with HTML, JSON, and plain text serializers.

LiveUpdates

SSE/WebSocket live data subscriptions with reconnection and auth support.

Quick start

import { FormBuilder } from '@codella-software/utils'
import { useFormBuilder } from '@codella-software/react'
import * as yup from 'yup'

type LoginForm = {
email: string
password: string
}

const builder = FormBuilder.create<LoginForm>()
.addFields([
{ name: 'email', type: 'email', label: 'Email' },
{ name: 'password', type: 'password', label: 'Password' },
])
.setInitialValues({ email: '', password: '' })
.setValidationSchema(
yup.object({
email: yup.string().email().required(),
password: yup.string().min(8).required(),
}),
)
.onSubmit(async (values) => {
await fetch('/api/login', { method: 'POST', body: JSON.stringify(values) })
})

export function LoginForm() {
const form = useFormBuilder({ builder })

return (
<form onSubmit={(e) => { e.preventDefault(); void form.submit() }}>
<input
value={form.values.email}
onChange={(e) => void form.setFieldValue('email', e.target.value)}
onBlur={() => form.setFieldTouched('email', true)}
/>
<input
type="password"
value={form.values.password}
onChange={(e) => void form.setFieldValue('password', e.target.value)}
onBlur={() => form.setFieldTouched('password', true)}
/>
<button type="submit" disabled={form.isSubmitting}>Sign in</button>
</form>
)
}

Next: Getting Started →