# How to Handle Form Validation Patterns with Astryx FieldStatus

> Master Astryx FieldStatus for form validation. Learn to use Field with validationPattern or control validation directly with custom props for seamless user experiences.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: how-to-guide
- Published: 2026-08-05

---

**Use the `Field` component with `validationPattern` for automatic validation, or render `FieldStatus` directly with `type`, `message`, and `variant` props for custom control.**

Astryx provides a dedicated **FieldStatus** component that centralizes visual feedback for form-field validation. The component integrates with the **Field** wrapper to display error, warning, or success states while ensuring WCAG-compliant accessibility through screen-reader announcements.

## Understanding the FieldStatus Architecture

The validation flow in Astryx follows a clear three-layer architecture implemented across [`packages/core/src/Field/Field.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Field/Field.tsx) and [`packages/core/src/FieldStatus/FieldStatus.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/FieldStatus/FieldStatus.tsx).

### 1. Validation Logic (Your Code)

Your application determines whether a value passes validation. This can use regex patterns, custom functions, or server-side checks.

### 2. Field Component (Wrapper Layer)

The `Field` component receives validation state through props: `status`, `statusMessage`, and `statusVariant`. According to [`packages/core/src/Field/Field.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Field/Field.tsx), these props forward directly to the internal `FieldStatus` renderer.

### 3. FieldStatus Component (Presentation Layer)

Located at [`packages/core/src/FieldStatus/FieldStatus.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/FieldStatus/FieldStatus.tsx), this component handles:
- Visual rendering (icons, colors, typography)
- **Variant selection**: `attached` (inlinebelow input) or `detached` (floating box)
- **Screen-reader announcements** via the `useAnnounce` hook
- **StyleX token application** for color-blind-safe theming

## Automatic Validation with Field

The simplest approach uses `Field`'s built-in `validationPattern` prop. The component derives `type`, `message`, and rendering automatically.

```tsx
import { Field } from '@astryx-design/core';

function UsernameInput() {
  const [value, setValue] = React.useState('');

  return (
    <Field
      label="Username"
      value={value}
      onChange={setValue}
      validationPattern={/^[a-zA-Z0-9]{3,12}$/}
      validationMessage="Username must be 3-12 alphanumeric characters."
    />
  );
}

```

When `validationPattern` fails, `Field` internally constructs:
- `status: 'error'`
- `statusMessage`: your custom message or default "Invalid input"
- Renders `FieldStatus` with appropriate styling

## Manual FieldStatus Control

For complex validation scenarios, render `FieldStatus` directly. This pattern appears in [`packages/core/src/FieldStatus/FieldStatus.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/FieldStatus/FieldStatus.tsx) as the core use case.

```tsx
import { FieldStatus } from '@astryx-design/core';

function EmailInput({ value, onChange }) {
  const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
  
  return (
    <div>
      <input 
        type="email" 
        value={value} 
        onChange={onChange}
        aria-describedby="email-status"
      />
      <FieldStatus
        id="email-status"
        type={isValid ? 'success' : 'error'}
        message={isValid 
          ? 'Email looks good!' 
          : 'Please enter a valid email address.'
        }
        variant="detached"
      />
    </div>
  );
}

```

## Variant Selection: Attached vs. Detached

The `variant` prop controls layout behavior as defined in the `FieldStatus` source:

| Variant | Behavior | Use Case |
|---------|----------|----------|
| **attached** | Renders as inline element below the input, inheriting field width | Compact forms, immediate feedback |
| **detached** | Floating box with icon, can be positioned independently | Complex forms, summary panels |

```tsx
// Attached variant example from packages/core/src/Field/Field.tsx usage
<Field
  label="Password"
  value={password}
  onChange={setPassword}
  status={strength === 'strong' ? 'success' : 'error'}
  statusMessage={strength === 'strong' 
    ? 'Strong password' 
    : 'Include uppercase and number'
  }
  statusVariant="attached"
/>

```

## Accessibility Implementation: useAnnounce Hook

The `FieldStatus` component imports `useAnnounce` from [`packages/core/src/hooks/useAnnounce.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useAnnounce.ts) to manage live-region announcements. This implementation detail ensures:

- Messages announce on initial mount
- Changes re-announce automatically
- Persistent live regions survive component unmounting

Per WCAG guidelines, the `detached` variant always renders:
- Visual icon (`aria-hidden="true"`)
- Text message (visible and announced)
- Color coding (supplemental, never sole indicator)

## Theming and Color Tokens

`FieldStatus` applies StyleX tokens defined in [`packages/core/src/FieldStatus/FieldStatus.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/FieldStatus/FieldStatus.tsx). Key token categories:

- `--color-error-muted` / `--color-error` / `--color-error-emphasis`
- `--color-warning-muted` / `--color-warning` / `--color-warning-emphasis`
- `--color-success-muted` / `--color-success` / `--color-success-emphasis`

These map to `type="error"`, `type="warning"`, and `type="success"` respectively.

## Complete Multi-Field Validation Example

```tsx
import { Field, FieldStatus } from '@astryx-design/core';

function RegistrationForm() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  const pwdStrong = /^(?=.*[A-Z])(?=.*\d).{8,}$/.test(password);

  return (
    <form>
      <Field
        label="Email"
        value={email}
        onChange={setEmail}
        status={email ? (emailValid ? 'success' : 'error') : undefined}
        statusMessage={emailValid ? 'Valid email' : 'Invalid email format'}
        statusVariant="attached"
      />
      
      <Field
        label="Password"
        value={password}
        onChange={setPassword}
        type="password"
      />
      
      {/* Conditional detached status for password requirements */}
      {password && !pwdStrong && (
        <FieldStatus
          type="warning"
          message="Password needs 8+ characters, 1 uppercase, 1 number"
          variant="detached"
        />
      )}
    </form>
  );
}

```

## Server-Side Validation Integration

For asynchronous validation, manually control `FieldStatus` props based on API responses:

```tsx
const [serverError, setServerError] = React.useState(null);

// After submit
const response = await validateOnServer(formData);
if (!response.valid) {
  setServerError(response.message);
}

// Render
{serverError && (
  <FieldStatus
    type="error"
    message={serverError}
    variant="detached"
  />
)}

```

## Summary

- **`Field` with `validationPattern`** provides automatic regex-based validation with minimal code
- **`FieldStatus` direct rendering** offers full control over validation logic and presentation
- **`variant="attached"`** integrates seamlessly with `Field` for inline feedback
- **`variant="detached"`** creates independent status displays for complex UIs
- **`useAnnounce` hook** ensures all validation states are announced to screen readers
- **StyleX tokens** guarantee color-blind-safe, theme-consistent styling

## Frequently Asked Questions

### How do I customize the error message for regex pattern validation?

Pass the `validationMessage` prop to `Field`. When `validationPattern` fails, this string replaces the default "Invalid input" message. For dynamic messages based on specific failure conditions, use manual `FieldStatus` rendering instead.

### Can FieldStatus announce to screen readers without visual display?

No. The `FieldStatus` component couples visual and auditory feedback by design. For invisible announcements, use the `useAnnounce` hook directly from [`packages/core/src/hooks/useAnnounce.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useAnnounce.ts) with `role="status"` or `role="alert"` on a visually hidden element.

### What happens if both attached and detached variants are used together?

The `Field` component renders only one `FieldStatus` instance internally. When using manual `FieldStatus` alongside `Field`, ensure unique `id` attributes and `aria-describedby` references to prevent duplicate announcements.