npm package: @kittlekit/schema-form-core
Description: Headless schema-driven form engine built on Zod
Latest version: 0.1.2

# @kittlekit/schema-form-core

[![npm version](https://img.shields.io/npm/v/@kittlekit/schema-form-core.svg)](https://www.npmjs.com/package/@kittlekit/schema-form-core)
[![license: MIT](https://img.shields.io/badge/license-MIT-green.svg)](../../LICENSE)

Headless schema-driven form engine for turning Zod schemas into form field definitions, defaults, visibility rules, normalized payloads, and validation results.

## What This Package Is

Use this package when you want the non-UI part of a schema form system:

- attach metadata to Zod fields
- derive field definitions from a Zod object schema
- build defaults for forms
- normalize values before submit
- evaluate conditional visibility
- validate payloads with Zod plus file requirements

This package does **not** render React components. It is the foundation for:

- `@kittlekit/schema-form-react`
- `@kittlekit/schema-form-tailwind`

## Install

```bash
npm install @kittlekit/schema-form-core zod
```

## Best Fit

Choose this package when you:

- want to keep your own form UI and only reuse schema logic
- want to use another form library on top of Zod-derived field definitions
- need form metadata/visibility/normalization outside React entirely

Choose a higher package when you want rendering:

- `@kittlekit/schema-form-react` for orchestration
- `@kittlekit/schema-form-tailwind` for ready-made styled field rendering

## Quick Start

```ts
import { z } from "zod"
import {
  fieldMeta,
  prepareFormDefaults,
  validateFormData,
  zodToFields,
} from "@kittlekit/schema-form-core"

const userSchema = z.object({
  name: z.string().describe(fieldMeta({ label: "Full name", colSpan: 6 })),
  email: z.string().email().describe(fieldMeta({ label: "Email" })),
  newsletter: z.boolean().describe(fieldMeta({ inputType: "switch" })),
})

const fields = zodToFields(userSchema)
const defaults = prepareFormDefaults(fields)

const result = validateFormData(userSchema, {
  name: "Ada Lovelace",
  email: "ada@example.com",
  newsletter: true,
}, fields)

if (result.success) {
  console.log(result.data)
}
```

## Core Concepts

### Metadata

There are two metadata styles supported:

- `fieldMeta(...)` for embedding metadata into `.describe(...)`
- `withMeta(...)` for attaching metadata directly to a Zod schema node

Example:

```ts
import { z } from "zod"
import { fieldMeta, when, withMeta } from "@kittlekit/schema-form-core"

const schema = z.object({
  role: z.string().describe(fieldMeta({
    label: "Role",
    inputType: "select",
    options: [
      { label: "Admin", value: "admin" },
      { label: "User", value: "user" },
    ],
  })),
  adminCode: withMeta(z.string(), {
    label: "Admin code",
    visibleWhen: when.equals("role", "admin"),
  }),
})
```

### Field Generation

`zodToFields(schema)` converts a Zod object schema into `FieldConfig[]`.

It understands:

- strings, emails, numbers, booleans
- enums and option metadata
- arrays
- multiselect/select/radio/autocomplete metadata
- file and map metadata
- visibility metadata

### Defaults and Normalization

Use:

- `buildDefaultValues(fields)` for empty defaults
- `prepareFormDefaults(fields, initialValues)` when editing an existing entity
- `normalizeValues(raw, fields)` before validation/submit
- `removeInternalFields(data)` to strip internal tracking values like `__localId`

### Validation

Use `validateFormData(schema, raw, fields)` to run the full pipeline:

1. normalize values
2. sanitize internal fields
3. validate against Zod
4. apply file requirements such as `minFiles`

## Important Exports

### Metadata and schema helpers

- `fieldMeta`
- `when`
- `withMeta`
- `getMeta`
- `hasMeta`
- `zodToFields`
- `parseZodSchema`

### Form preparation

- `buildDefaultValues`
- `prepareFormDefaults`
- `addLocalIdsToArrayItems`
- `normalizeValues`
- `removeInternalFields`

### Visibility and options

- `evaluateCondition`
- `evaluateConditions`
- `isFieldVisible`
- `filterVisibleFields`
- `resolveFieldOptions`
- `sortFields`

### Validation

- `validateWithZod`
- `validateFileRequirements`
- `validateFormData`
- `getFieldErrors`

## Typical Integration Patterns

### Use with your own UI

```ts
const fields = zodToFields(schema)

fields.forEach((field) => {
  // render field with your own component system
})
```

### Use with your own form state library

You can combine this package with:

- `react-hook-form`
- Formik
- TanStack Form
- custom state reducers

### Use with Kittlekit packages

- `schema-form-react` adds orchestration/hooks
- `schema-form-tailwind` adds a ready-made renderer

## Runtime Notes

- ESM package
- requires modern bundler/runtime with `import` support
- publishes runtime JS and `.d.ts` types
- works in both JavaScript and TypeScript consumers

## Out of Scope

This package intentionally does not include:

- React hooks
- rendered form components
- Tailwind classes
- upload transport
- map implementation
- Next.js-specific runtime APIs

## Package Relationship

Use the full stack like this:

```text
schema-form-core -> schema-form-react -> schema-form-tailwind
                                   -> schema-form-upload-next
                                   -> schema-form-map-leaflet
```

## Publish

```bash
npm run build --workspace @kittlekit/schema-form-core
npm publish --workspace @kittlekit/schema-form-core --access public
```

## Examples

- smoke app: `sandbox/schema-form-smoke`
- app wrapper reference: `web/src/components/form/SchemaForm.tsx`

## Release Notes

### 0.1.1

- switched package licensing to MIT
- stabilized core exports for metadata, defaults, visibility, and validation
- validated npm consumption in a clean smoke app