npm.io
0.0.0 • Published 2d ago

form-os

Licence
Version
0.0.0
Deps
0
Size
134 kB
Vulns
0
Weekly
0

form-os

A small, cross-framework form config library.

  • @form-os/core — dependency-free config parser, dependency engine, and validation helpers.
  • @form-os/reactreact-hook-form adapter with tree-shakeable field components.
  • @form-os/schema — JSON Schema and TypeScript types for form configs.
  • @form-os/svelte — placeholder for a future Svelte adapter.

Install

pnpm add @form-os/core @form-os/react react-hook-form
# optional
pnpm add @form-os/schema

React and react-hook-form are peer dependencies of @form-os/react.

Define a form config

import type { FormConfig } from '@form-os/core';

const config: FormConfig = {
  title: 'Contact',
  submitLabel: 'Send',
  fields: [
    { name: 'name', type: 'text', label: 'Name', rules: { required: true } },
    { name: 'email', type: 'email', label: 'Email', rules: { required: true } },
    {
      name: 'reason',
      type: 'select',
      label: 'Reason',
      options: [
        { label: 'Feedback', value: 'feedback' },
        { label: 'Support', value: 'support' },
      ],
    },
    {
      name: 'details',
      type: 'textarea',
      label: 'Details',
      dependencies: [
        { name: 'reason', value: 'support', type: 'show' },
      ],
    },
  ],
};

Use with React

import { FormProvider } from '@form-os/react';
import { DefaultField, useFormConfig } from '@form-os/react/fields';

function MyForm() {
  const { handleSubmit, fields, ...form } = useFormConfig(config);

  const onSubmit = (data: any) => console.log(data);

  return (
    <FormProvider {...form}>
      <form onSubmit={handleSubmit(onSubmit)}>
        {fields.map((field) => (
          <DefaultField key={field.name} field={field} />
        ))}
        <button type="submit">Send</button>
      </form>
    </FormProvider>
  );
}

Custom field components

import { Field, useFormConfig } from '@form-os/react';
import { TextField, defaultComponents } from '@form-os/react/fields';

const components = {
  ...defaultComponents,
  text: (props) => (
    <div>
      <label>{props.fieldConfig.label}</label>
      <TextField {...props} />
      {props.error && <span>{props.error.message}</span>}
    </div>
  ),
};

<Field field={config.fields[0]} components={components} />

Use the core without React

import { normalizeConfig, createDefaultValues, getFieldState } from '@form-os/core';

const { fields } = normalizeConfig(config);
const defaults = createDefaultValues(fields);
const state = getFieldState(fields[0], defaults);
// state.visible, state.required, state.disabled, state.defaultValue

Validate against the JSON schema

import { schema } from '@form-os/schema';
import Ajv from 'ajv';

const ajv = new Ajv();
const validate = ajv.compile(schema);

if (!validate(config)) {
  console.error(validate.errors);
}

Scripts

pnpm install
pnpm build      # build every package
pnpm test       # run the test suite
pnpm typecheck  # run tsc --noEmit in every package