Skip to main content

Form skill

The bestax-form skill teaches an agent to build forms with the bestax form components. There is no form library — state is plain React, and errors surface via color="danger" + message + messageColor.

Install

npx skills add https://github.com/allxsmith/bestax --skill bestax-form

Signup & validation

Skill bestax-formModel Claude Opus 4.8Generated 2026-06-27

Prompt

Build a signup form (name, email, country) that validates on submit and shows
inline errors — no form library — following the bestax form skill.

Live form

Click Create account with empty fields to see the validation.

function example() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [country, setCountry] = useState('');
  const [submitted, setSubmitted] = useState(false);

  const errors = {
    name: !name.trim() ? 'Name is required.' : undefined,
    email: !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)
      ? 'Enter a valid email.'
      : undefined,
    country: !country ? 'Pick a country.' : undefined,
  };
  const show = k => (submitted ? errors[k] : undefined);

  return (
    <form
      onSubmit={e => {
        e.preventDefault();
        setSubmitted(true);
      }}
      style={{ maxWidth: '28rem' }}
    >
      <Input
        label="Name"
        value={name}
        onChange={e => setName(e.target.value)}
        color={show('name') ? 'danger' : undefined}
        message={show('name')}
        messageColor="danger"
        iconLeftName="user"
      />
      <Input
        label="Email"
        type="email"
        value={email}
        onChange={e => setEmail(e.target.value)}
        color={show('email') ? 'danger' : undefined}
        message={show('email')}
        messageColor="danger"
        iconLeftName="envelope"
      />
      <Select
        label="Country"
        value={country}
        onChange={e => setCountry(e.target.value)}
        color={show('country') ? 'danger' : undefined}
        message={show('country')}
        messageColor="danger"
      >
        <option value="">Select…</option>
        <option value="us">United States</option>
        <option value="ca">Canada</option>
        <option value="uk">United Kingdom</option>
      </Select>
      <Button color="primary" type="submit">
        Create account
      </Button>
    </form>
  );
}

Validation pattern

Own the state, compute errors, and reflect them on each field with color="danger" (colors the input), message (the help text), and messageColor="danger" (colors the help text).

Account settings

Skill bestax-formModel Claude Opus 4.8Generated 2026-06-27

Prompt

Build an account settings form with a bio, a timezone select, an email-digest
radio group, interest checkboxes, and toggle switches — following the bestax
form skill.

Live form

function example() {
  const [bio, setBio] = useState('');
  const [timezone, setTimezone] = useState('utc');
  const [digest, setDigest] = useState('weekly');
  const [interests, setInterests] = useState(['react']);
  const [darkMode, setDarkMode] = useState(true);
  const [twoFactor, setTwoFactor] = useState(false);

  return (
    <form style={{ maxWidth: '32rem' }}>
      <TextArea
        label="Bio"
        value={bio}
        onChange={e => setBio(e.target.value)}
        rows={3}
        placeholder="Tell us about yourself"
      />
      <Select
        label="Timezone"
        value={timezone}
        onChange={e => setTimezone(e.target.value)}
      >
        <option value="utc">UTC</option>
        <option value="est">Eastern</option>
        <option value="pst">Pacific</option>
      </Select>
      <Field label="Email digest">
        <Radios name="digest" value={digest} onChange={setDigest}>
          <Radio value="daily">Daily</Radio>
          <Radio value="weekly">Weekly</Radio>
          <Radio value="never">Never</Radio>
        </Radios>
      </Field>
      <Field label="Interests">
        <Checkboxes name="interests" value={interests} onChange={setInterests}>
          <Checkbox value="react">React</Checkbox>
          <Checkbox value="bulma">Bulma</Checkbox>
          <Checkbox value="design">Design</Checkbox>
        </Checkboxes>
      </Field>
      <Field>
        <Switch
          checked={darkMode}
          onChange={e => setDarkMode(e.target.checked)}
          color="primary"
        >
          Dark mode
        </Switch>
      </Field>
      <Field>
        <Switch
          checked={twoFactor}
          onChange={e => setTwoFactor(e.target.checked)}
          color="success"
        >
          Two-factor authentication
        </Switch>
      </Field>
      <Button color="primary">Save settings</Button>
    </form>
  );
}

Grouped controls

Checkboxes manages an array value; Radios manages a single value. Each child carries a value, and the group reports the selection through onChange.

Advanced inputs

Skill bestax-formModel Claude Opus 4.8Generated 2026-06-27

Prompt

Build a freelancer profile form using the advanced inputs: autocomplete, slider,
number input, star rating, and tag input — following the bestax form skill.

Live form

function example() {
  const [skill, setSkill] = useState('');
  const [experience, setExperience] = useState(5);
  const [rateUsd, setRateUsd] = useState(75);
  const [quality, setQuality] = useState(4);
  const [tags, setTags] = useState(['typescript']);

  return (
    <div style={{ maxWidth: '32rem' }}>
      <Autocomplete
        label="Primary skill"
        value={skill}
        onInput={setSkill}
        onSelect={item =>
          setSkill(typeof item === 'string' ? item : (item?.value ?? ''))
        }
        data={['React', 'Vue', 'Svelte', 'Angular']}
        openOnFocus
      />
      <Slider
        label="Years of experience"
        value={experience}
        onChange={setExperience}
        min={0}
        max={20}
        tooltip="auto"
      />
      <Numberinput
        label="Hourly rate (USD)"
        value={rateUsd}
        onChange={setRateUsd}
        min={10}
        max={500}
        step={5}
      />
      <Rate
        label="Self-rated quality"
        value={quality}
        onChange={setQuality}
        max={5}
      />
      <Taginput
        label="Tech stack"
        value={tags}
        onChange={next =>
          setTags(next.map(t => (typeof t === 'string' ? t : t.value)))
        }
        data={['typescript', 'node', 'graphql', 'css']}
      />
    </div>
  );
}

Wiring notes

Autocomplete reports typing via onInput and a chosen suggestion via onSelect. Taginput's onChange yields tag objects-or-strings, so map them when you store plain strings. A dual-thumb Slider needs the range prop.

Files & scheduling

Skill bestax-formModel Claude Opus 4.8Generated 2026-06-27

Prompt

Build an onboarding form with a file upload plus date, time, and date-time
pickers — following the bestax form skill.

Live form

function example() {
  const [date, setDate] = useState(null);
  const [time, setTime] = useState(null);
  const [appt, setAppt] = useState(null);

  return (
    <div style={{ maxWidth: '28rem' }}>
      <Field label="Profile photo">
        <File hasName buttonLabel="Choose a file…" />
      </Field>
      <DateInput label="Date of birth" value={date} onChange={setDate} />
      <TimeInput
        label="Preferred contact time"
        value={time}
        onChange={setTime}
      />
      <DateTimeInput label="Appointment" value={appt} onChange={setAppt} />
    </div>
  );
}

Controlled date & time

The date/time inputs are controlled with a Date | null value and an onChange(date) handler, just like the text inputs — only the widget differs.