How to Handle Form Validation Patterns with Astryx FieldStatus

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 and 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, these props forward directly to the internal FieldStatus renderer.

3. FieldStatus Component (Presentation Layer)

Located at 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.

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 as the core use case.

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
// 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 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. 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

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:

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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →