A lightweight, extensible dynamic form engine for React and Angular, built for scalable applications and design systems.
dynamic-field-kit lets you define forms using configuration objects instead of hard-coded UI, and allows applications to freely extend field types across frameworks without modifying the library. Register custom renderers in React, Angular, or vanilla JS using a shared field registry.
- Schema-driven dynamic forms
- Extensible field types (no enums, no hard-coded unions)
- Pluggable field renderers via shared registry
- Runtime conditional fields (
appearCondition) - Clean TypeScript declarations (DTS-safe)
- Framework-agnostic core (works with React, Angular, Vue, or vanilla JS)
- Ideal for form builders & design systems
| Package | Description |
|---|---|
@dynamic-field-kit/core |
Core types and shared field registry |
@dynamic-field-kit/react |
React components (FieldInput, MultiFieldInput, DynamicInput) |
@dynamic-field-kit/angular |
Angular components and module (standalone + NgModule) |
@dynamic-field-kit/vue |
Vue 3 components and module |
For React:
npm install @dynamic-field-kit/core @dynamic-field-kit/reactFor Angular:
npm install @dynamic-field-kit/core @dynamic-field-kit/angularFor Vue:
npm install @dynamic-field-kit/core @dynamic-field-kit/vueThe library does NOT define field types like:
"text" | "number"Instead, it exposes an extendable interface that applications can augment:
export interface FieldTypeMap {}This allows:
- Unlimited custom field types
- Strong typing without locking consumers
- No need to rebuild the library
This pattern is used by mature libraries like MUI, React Hook Form, and Redux Toolkit.
Create a .d.ts file in your app (e.g. src/types/dynamic-field.d.ts):
import "@dynamic-field-kit/core"
declare module "@dynamic-field-kit/core" {
interface FieldTypeMap {
text: string
number: number
checkbox: boolean
select: string
}
}export interface FieldRendererProps<T = any> {
value?: T
onValueChange?: (value: T) => void
label?: string
}👉 A common contract for all field renderers
A FieldDescription defines what a field is, not how it looks.
import { FieldDescription } from "@dynamic-field-kit/core"
const fields: FieldDescription[] = [
{
name: "username",
type: "text",
label: "Username"
},
{
name: "age",
type: "number",
label: "Age",
appearCondition: (data) => data.username !== ""
}
]Common Properties
| Property | Description |
|---|---|
| name | Field key in form data |
| type | Field renderer key |
| label | UI label |
| value | Default value |
| appearCondition | Runtime visibility condition |
Field Registry (Render Layer)
The library does not ship UI components. Instead, applications register their own renderers using the fieldRegistry from the framework adapter (@dynamic-field-kit/react, @dynamic-field-kit/angular, etc.).
import { fieldRegistry } from "@dynamic-field-kit/react" // or /angular
fieldRegistry.register("text", myTextRenderer)
fieldRegistry.register("checkbox", myCheckboxRenderer)For detailed setup and component API:
- React: See
packages/react/README.md - Angular: See
packages/angular/README.md - Vue: See
packages/vue/README.md - Core concepts: See
packages/core/README.md
You do not need to modify the library. Just extend FieldTypeMap in your application:
declare module "@dynamic-field-kit/core" {
interface FieldTypeMap {
date: Date
myCustom: any
}
}Then register renderers using the framework-specific adapter:
- React:
import { fieldRegistry } from "@dynamic-field-kit/react" - Angular:
import { fieldRegistry } from "@dynamic-field-kit/angular" - Vue:
import { fieldRegistry } from "@dynamic-field-kit/vue"
Now your custom types are fully type-safe throughout the codebase.
The library intentionally avoids enforcing domain types. If you want strict typing, cast inside your app:
interface UserForm {
age: number
}
const fields: FieldDescription[] = [
{
name: "age",
type: "number",
appearCondition: (data) =>
(data as UserForm).age > 18
}
]This keeps the library generic while allowing strict typing in the app.
The monorepo contains framework-agnostic core and framework-specific adapters:
dynamic-field-kit (monorepo)
├─ packages/
│ ├─ core # Framework-agnostic types and registry
│ ├─ react # React components & DynamicInput
│ ├─ angular # Angular components & DynamicFieldKitModule
│ └─ vue # Vue 3 components
├─ example/ # Demo apps and integration guides
└─ .github/ # Copilot AI agent instructions
All packages share the same fieldRegistry instance, so registrations are visible across frameworks (in the same process).
This library intentionally does not include:
- Built-in UI components (bring your own renderers)
- Form state management
- Validation logic
It is a form engine, not a full form framework.
MIT © vannt-dev
Contributions welcome! Please see individual package READMEs for setup and development instructions.