Intro

First lets take a look at whats possible with informed. Below is an example where we wrapped informed around Adobe's desgin-system.
Those components got defined once inside `YourComponents` directory. You will see how this was done later inside the setup docs.

Example:

Car
{
  "valid": true,
  "pristine": true,
  "dirty": false,
  "values": {
    "phone": "+1 (123)-456-7899",
    "car": "ms"
  },
  "errors": {}
}

Code:

// Example.jsx

import { Relevant, Debug } from 'informed';

// Inputs that were hooked up to informed via "useField"
import { Form, Input, Select, Checkbox, Option, Button } from 'YourComponents';

const onSubmit = ({ values }) => window.alert(`Hello ${values.name}`);

const Example = () => (
  <Form onSubmit={onSubmit} initialValues={{ phone: '1234567899' }}>
    <Input name="name" label="Name" placeholder="Elon" required />
    <Input name="age" type="number" label="Age" required="Age Required" />
    <Input name="phone" label="Phone" formatter="+1 (###)-###-####" />
    <Select name="car" label="Car" initialValue="ms">
      <Option key="ms">Model S</Option>
      <Option key="m3">Model 3</Option>
      <Option key="mx">Model X</Option>
      <Option key="my">Model Y</Option>
    </Select>
    <Checkbox name="married" label="Married?" />
    <Relevant when={({ formState }) => formState.values.married}>
      <Input name="spouse" label="Spouse" />
    </Relevant>
    <Button type="submit">Submit</Button>
    <Debug valid pristine dirty values errors />
  </Form>
);

export default Example;
Whats going on here? Basically informed manages form state for you, and allows you to easily hook ito its state managemenet with the use ofuseField

Above, we imported inputs that were already wrapped and simply rendered them on the page.

This is the way! You should most of the time not even realize you are using a form library 😁

Capabilities

Informed is cabable of A LOT of cool things! The below example shows off many of those cababilites in one form.

  • Arrays: ability to render dynamic arrays of fields [ 'a', 'b' ] or [ { name: 'Joe', age: 29 }, { name: 'Hope', age: 24 }]
  • Relevance: ability to render fields conditionally depending on the state of other parts of the form
  • JSPAN: ability to easily and intuitively manipulate form state
  • Formatting: ability to perform display formatting, where the format shown to user can differ from the state of the values stored
  • Validation: ability to perform both synchronous and asynchronous validation in a controlled manner
  • Api: ability to manipulate the form state both inside and outside the context of the form
  • State: ability to access field and form data
  • Multistep: ability to create dynamic multistep forms
  • Scope: ability to scope ( group ) fields
  • Schema: ability to render forms based on pure JSON schema
  • Dynaic: ability to hide and show fields ( render and unrender ) and either cleanup or maintain state of unmounted fields
  • Debugging: ability to easily debug users state as well as internals of the library
  • Nesting: ability to have highly nested value structure state.values.friends[1].brother.parents.cars[0].model

Example:

Car

Children

What car do they drive?

MarketSupplyPrice
US
CA
{
  "pristine": true,
  "dirty": false,
  "submitted": false,
  "invalid": false,
  "valid": true,
  "submitting": false,
  "validating": 0,
  "gathering": 0,
  "values": {
    "phone": "+1 (123)-456-7899",
    "salary": 80000.25,
    "love": true,
    "car": "ms",
    "mother": {
      "name": "Maye",
      "age": 74
    },
    "children": [
      {
        "name": "Jake",
        "age": "18"
      },
      {
        "name": "Joy",
        "age": "15"
      }
    ],
    "US": {
      "supply": 100,
      "price": 40000
    },
    "CA": {
      "supply": 50,
      "price": 42000
    }
  },
  "errors": {},
  "touched": {},
  "maskedValues": {
    "phone": "+1 (123)-456-7899",
    "salary": "$80,000.25",
    "love": true,
    "car": "ms",
    "mother": {
      "name": "Maye",
      "age": 74
    },
    "children": [
      {
        "name": "Jake",
        "age": "18"
      },
      {
        "name": "Joy",
        "age": "15"
      }
    ],
    "US": {
      "supply": 100,
      "price": 40000
    },
    "CA": {
      "supply": 50,
      "price": 42000
    }
  },
  "dirt": {},
  "focused": {},
  "modified": {},
  "data": {},
  "initialValues": {
    "phone": "1234567899",
    "salary": 80000.25
  },
  "disabled": false,
  "memory": {}
}

Code:

// Example.jsx
import {
  Debug,
  Relevant,
  ArrayField,
  Scope,
  FormFields,
  utils
} from 'informed';
import { Form, Input, Select, Checkbox, Option, Button } from 'YourComponents';

/* ----------------- On Submit Gets Entire Form State ------------------ */
const onSubmit = ({ values }) => window.alert(`Hello ${values.name}`);

/* ------------------- Custom Field Level Validation ------------------- */
const validatePassword = (value, { password1, password2 }) => {
  if (password1 !== password2) return 'Passwords must match';
};

/* ----------------------- Intl Number Formatter ----------------------- */
const { formatter, parser } = utils.createIntlNumberFormatter('en-US', {
  style: 'currency',
  currency: 'USD'
});

const Example = () => (
  /* -------------------- Form Level initialValues --------------------- */
  <Form
    onSubmit={onSubmit}
    initialValues={{ phone: '1234567899', salary: 80_000.25 }}>
    {/* ------------------ Built in validation props ------------------- */}
    <Input name="name" label="Name" required />

    {/* --------------- Override default error messages ---------------- */}
    <Input name="age" type="number" label="Age" required="Age Required" />

    {/* ----------------------- Format inputs -------------------------- */}
    <Input name="phone" label="Phone" formatter="+1 (###)-###-####" />
    <Input name="salary" label="Salary" formatter={formatter} parser={parser} />

    {/* --------------------- Paried Validation ------------------------ */}
    <Input
      name="password1"
      label="Password"
      required
      minLength={8}
      validate={validatePassword}
      validateOn="change"
      showErrorIfError
      validateWhen={['password2']}
    />
    <Input
      name="password2"
      label="Confirm password"
      required
      minLength={8}
      validate={validatePassword}
      validateOn="change"
      showErrorIfError
      validateWhen={['password1']}
    />

    {/* ------------------ Field Level default value  ------------------- */}
    <Checkbox name="love" label="Do you love Tesla?" defaultValue={true} />

    {/* ------------------- Field Level initial value ------------------- */}
    <Select name="car" label="Car" initialValue="ms">
      <Option key="ms">Model S</Option>
      <Option key="m3">Model 3</Option>
      <Option key="mx">Model X</Option>
      <Option key="my">Model Y</Option>
    </Select>

    {/* --------------------------- Relevance --------------------------- */}
    <Checkbox name="married" label="Are you Married?" />
    <Relevant when={({ formState }) => formState.values.married}>
      <Input name="spouse" label="Spouse" required />
    </Relevant>

    {/* ---------------------------- Scope ------------------------------ */}
    <Scope scope="mother">
      <Input name="name" label="First name:" initialValue="Maye" />
      <Input name="name" label="Last name:" initialValue="Musk" />
      <Input name="age" label="Age:" type="number" initialValue={74} />
    </Scope>

    {/* ------------------------- Array Fields -------------------------- */}
    <br />
    <h3>Children</h3>
    <ArrayField
      name="children"
      initialValue={[
        {
          name: 'Jake',
          age: '18'
        },
        {
          name: 'Joy',
          age: '15'
        }
      ]}>
      {({ add }) => (
        <>
          <Button onClick={add} type="button" variant="accent" style="outline">
            Add
          </Button>
          <ArrayField.Items>
            {({ remove, name }) => (
              <>
                <Input name="name" label="Name" required />
                <Input name="age" label="Age" type="number" />

                {/* ----------------- Scoped Relevance ------------------ */}
                <Relevant
                  when={({ formApi, scope }) =>
                    formApi.getValue(`${scope}.age`) >= 16
                  }>
                  <Select name="car" label="What car do they drive?">
                    <Option key="ms">Model S</Option>
                    <Option key="m3">Model 3</Option>
                    <Option key="mx">Model X</Option>
                    <Option key="my">Model Y</Option>
                  </Select>
                </Relevant>
                <Button type="button" onClick={remove} variant="negative">
                  Remove
                </Button>
              </>
            )}
          </ArrayField.Items>
        </>
      )}
    </ArrayField>
    <br />

    {/* ------------------------- Table Forms -------------------------- */}
    <table className="form-table">
      <thead>
        <tr>
          <th>Market</th>
          <th>Supply</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          {/* --------------- Scope using Scope --------------- */}
          <Scope scope="US">
            <td>US</td>
            <td>
              <Input name="supply" type="number" initialValue={100} />
            </td>
            <td>
              <Input name="price" type="number" initialValue={40_000} />
            </td>
          </Scope>
        </tr>
        {/* ------------- Scope using path syntax ------------- */}
        <tr>
          <td>CA</td>
          <td>
            <Input name="CA.supply" type="number" initialValue={50} />
          </td>
          <td>
            <Input name="CA.price" type="number" initialValue={42_000} />
          </td>
        </tr>
      </tbody>
    </table>

    {/* ------------------- JSON Schema Rendering -------------------- */}
    <FormFields
      schema={{
        type: 'object',
        properties: {
          bio: {
            type: 'string',
            title: 'Bio',
            'ui:control': 'textarea'
          },
          birthday: {
            type: 'text',
            title: 'Birthday',
            'ui:control': 'input',
            'ui:props': {
              type: 'date'
            }
          }
        }
      }}
    />

    {/* ------- Automaically calls onSubmit for submit button ---------- */}
    <Button type="submit" variant="accent" style="fill">
      Submit
    </Button>

    {/* ----------------------- Debug Component ------------------------- */}
    <Debug />
  </Form>
);

export default Example;

How do I start!?

In order to use informed all you need to do is wrap your own inputs with informed via a `useField` hook.
You do this one time and then can use your form inputs as normal JSX or via schema! Lets go get SETUP