# Validation

```ts
import { ValidationGroup, Validator, ValidationError } from 'cx/widgets';
```



CxJS provides a comprehensive validation system for form fields. The main components are:

| Component | Description |
| --------- | ----------- |
| [ValidationGroup](validation-group) | Container that tracks validation state of all fields inside it. Propagates settings like `disabled`, `readOnly`, or `asterisk` to children |
| [Validator](validator) | Performs custom validation on computed or combined values. Use for cross-field or complex validation |
| [ValidationError](validation-error) | Displays validation error messages. Renders as a `<label>` linked to the invalid field |

## Field-Level Validation

Form fields indicate invalid input by providing visual feedback such as changing border colors, displaying tooltips, or showing error messages.

By default, inputs change colors and show an error tooltip. Click on the field below, then click somewhere else to see the change. Hovering over the input afterwards shows the tooltip.

```tsx
import { createModel } from "cx/data";
import { LabelsLeftLayout } from "cx/ui";
import { enableTooltips, TextField } from "cx/widgets";

enableTooltips();

interface Model {
  name: string;
}

const m = createModel<Model>();

export default (
  <LabelsLeftLayout>
    <TextField label="Name" value={m.name} placeholder="Required" required />
  </LabelsLeftLayout>
);

```

## Asterisk and Visited

Required fields can be highlighted by setting the `asterisk` flag. Alternatively, fields can be marked as `visited` which forces them to show validation errors immediately.

```tsx
import { createModel } from "cx/data";
import { LabelsLeftLayout } from "cx/ui";
import { enableTooltips, TextField } from "cx/widgets";

enableTooltips();

interface Model {
  asterisk: string;
  visited: string;
}

const m = createModel<Model>();

export default (
  <LabelsLeftLayout>
    <TextField label="Asterisk" value={m.asterisk} placeholder="Required" required asterisk />
    <TextField label="Visited" value={m.visited} placeholder="Required" required visited />
  </LabelsLeftLayout>
);

```

## Help Property

Form fields provide the `help` property which can display additional information next to the field. This can be text, a button, or a `ValidationError` component.

```tsx
import { createModel } from "cx/data";
import { LabelsLeftLayout } from "cx/ui";
import {
  TextField,
  ValidationGroup,
  ValidationError,
  Button,
} from "cx/widgets";
import "../../icons/lucide";

interface Model {
  help: string;
  helpButton: string;
  helpError: string;
}

const m = createModel<Model>();

export default (
  <ValidationGroup>
    <LabelsLeftLayout>
      <TextField
        label="Help Text"
        value={m.help}
        help={<span class="text-sm text-gray-500">Additional info</span>}
      />
      <TextField
        label="Help Button"
        value={m.helpButton}
        help={<Button icon="calculator" mod="hollow" />}
      />
      <TextField
        label="Help Error"
        value={m.helpError}
        required
        visited
        help={ValidationError}
      />
    </LabelsLeftLayout>
  </ValidationGroup>
);

```

## Validation Modes

The `validationMode` property controls how validation errors are displayed:

- **`tooltip`** (default) - Errors appear in a tooltip when hovering over the field
- **`help`** - Errors appear inline next to the field
- **`help-block`** - Errors appear as a block element below the field

```tsx
import { createModel } from "cx/data";
import { LabelsLeftLayout } from "cx/ui";
import { enableTooltips, TextField, ValidationGroup } from "cx/widgets";

enableTooltips();

interface Model {
  tooltip: string;
  help: string;
  helpBlock: string;
}

const m = createModel<Model>();

export default (
  <ValidationGroup>
    <LabelsLeftLayout>
      <TextField
        label="Tooltip (default)"
        value={m.tooltip}
        required
        minLength={5}
        visited
        validationMode="tooltip"
      />
      <TextField
        label="Help"
        value={m.help}
        required
        minLength={5}
        visited
        validationMode="help"
      />
      <TextField
        label="Help Block"
        value={m.helpBlock}
        required
        minLength={5}
        visited
        validationMode="help-block"
      />
    </LabelsLeftLayout>
  </ValidationGroup>
);

```

## Custom Validation

Form fields accept validation callback functions through `onValidate`. Use `reactOn` to control when validation triggers.

```tsx
import { createModel } from "cx/data";
import { LabelsTopLayout } from "cx/ui";
import { TextField, ValidationGroup } from "cx/widgets";

interface Model {
  framework: string;
}

const m = createModel<Model>();

export default (
  <ValidationGroup>
    <LabelsTopLayout vertical>
      <TextField
        label="Favorite framework?"
        value={m.framework}
        validationMode="help-block"
        reactOn="enter blur"
        onValidate={(v) => {
          if (v != "CxJS") return "Oops, wrong answer!";
        }}
      />
    </LabelsTopLayout>
  </ValidationGroup>
);

```

## Async Validation

`onValidate` can return a Promise for server-side validation. Use `FirstVisibleChildLayout` to show either the error or a success indicator.

```tsx
import { createModel } from "cx/data";
import { FirstVisibleChildLayout, LabelsTopLayout } from "cx/ui";
import { TextField, ValidationGroup, ValidationError, Icon } from "cx/widgets";
import "../../icons/lucide";

interface Model {
  username: string;
}

const m = createModel<Model>();

export default (
  <ValidationGroup>
    <LabelsTopLayout vertical>
      <TextField
        label="Username"
        value={m.username}
        required
        visited
        onValidate={(v) =>
          new Promise((resolve) => {
            setTimeout(() => {
              resolve(v == "cx" ? "This name is taken." : false);
            }, 500);
          })
        }
        help={
          <FirstVisibleChildLayout>
            <ValidationError />
            <Icon name="check" class="text-green-600" />
          </FirstVisibleChildLayout>
        }
      />
    </LabelsTopLayout>
  </ValidationGroup>
);

```