No Preview

Sorry, but you either have no stories or none are selected somehow.

If the problem persists, check the browser console, or the terminal you've run Storybook from.

Custom Inputs

Sometimes the inputs informed provides are not good enough. So we decided to help you out with that! Informed also gives you access to an useField Hook.

Custom Text Input

Lets say you like informed's text input but you want to show an error and turn it red when there is an error. You could achieve this with the following code.

{}
{}
import { Form, useField } from 'informed';

const ErrorText = props => {
  const { fieldState, fieldApi, render, ref, userProps } = useField(props);

  const { value } = fieldState;
  const { setValue, setTouched } = fieldApi;
  const { onChange, onBlur, ...rest } = userProps;
  return render(
    <React.Fragment>
      <input
        {...rest}
        ref={ref}
        value={!value && value !== 0 ? '' : value}
        onChange={e => {
          setValue(e.target.value);
          if (onChange) {
            onChange(e);
          }
        }}
        onBlur={e => {
          setTouched(true);
          if (onBlur) {
            onBlur(e);
          }
        }}
        style={fieldState.error ? { border: 'solid 1px red' } : null}
      />
      {fieldState.error ? (
        <small style={{ color: 'red' }}>{fieldState.error}</small>
      ) : null}
    </React.Fragment>
  );
};

const validate = value => {
  return !value || value.length < 5
    ? 'Field must be at least five characters'
    : undefined;
};

<Form id="custom-form">
  <label>
    First name:
    <ErrorText field="name" validateOnChange validateOnBlur />
  </label>
  <button type="submit">Submit</button>
</Form>;

But it gets easier!

Here we use the hook useField, BUT, instead of hooking it all up ourselves, we just spread the informed object onto the input. Its important to note the use of fieldType, when you want to use the informed object, you need to tell the hook what type of input your dealing with so it knows how to handle the dom or native event.

{}
{}
import { Form, useField } from 'informed';

const validate = value => {
  return !value || value.length < 5
    ? 'Field must be at least five characters'
    : undefined;
};

const ErrorText = (props) => {

  const { render, informed, fieldState } = useField({ fieldType: 'text', validate, ...props });

  return render(
    <>
      <input
        {...informed}
        style={fieldState.error ? { border: 'solid 1px red' } : null}
      />
      {fieldState.error ? (
        <small style={{ color: 'red' }}>{fieldState.error}</small>
      ) : null}
    </>
  );
};

<Form id="custom-form">
  <label>
    First name:
    <ErrorText
      field="name"
      validateOnChange
      validateOnBlur
    />
  </label>
  <button type="submit">Submit</button>
</Form>;

Endless Possibilities

Because of this design, you can add your very own custom inputs! Below is an example of a slider!

{
  "range": "50"
}
import { Form, asField } from 'informed';

const Slider = asField(({ fieldState, fieldApi, ...props }) => {
  const { value } = fieldState;
  const { setValue, setTouched } = fieldApi;
  const { onChange, onBlur, initialValue, forwardedRef, ...rest } = props;
  return (
    <input
      {...rest}
      type="range"
      min={0}
      max={100}
      step={5}
      ref={forwardedRef}
      value={value || initialValue || '0'}
      onChange={e => {
        setValue(e.target.value);
        if (onChange) {
          onChange(e);
        }
      }}
      onBlur={e => {
        setTouched(true);
        if (onBlur) {
          onBlur(e);
        }
      }}
    />
  );
});

<Form id="custom-form">
  <label htmlFor="custom-range">Range:</label>
  <Slider field="range" id="custom-range" initialValue={50} />
  <button type="submit">Submit</button>
</Form>;