EXEPERTAI LAB

Research alpha

Time Machine
EXEPERTAI LAB
GALLERY / COLLECTION
← Browse Astryx gallery
Field·component·@astryxdesign/core

Field

Low-level form field wrapper for custom controls that need a label, description, and optional/required indicators.

Open in Playground @astryxdesign/core/Field

Showcases and examples

4 documented examples

Field

A form field wrapping a text input with a label, description, and validation status.

Preview loads on approachPreview loads on approach
Exact source · field-showcase
// Copyright (c) Meta Platforms, Inc. and affiliates.

'use client';

import {useState} from 'react';
import {Field} from '@astryxdesign/core/Field';
import {TextInput} from '@astryxdesign/core/TextInput';
import {Stack} from '@astryxdesign/core/Layout';

export default function FieldShowcase() {
  const [email, setEmail] = useState('');

  const status =
    email.length > 0 && !email.includes('@')
      ? {type: 'error' as const, message: 'Enter a valid email address.'}
      : undefined;

  return (
    <Stack direction="vertical" gap={3} style={{width: 320}}>
      <Field
        label="Email"
        inputID="field-email"
        description="We will never share your email."
        isRequired
        status={status}>
        <TextInput
          label="Email"
          isLabelHidden
          value={email}
          onChange={setEmail}
          placeholder="you@example.com"
        />
      </Field>
    </Stack>
  );
}

Usage

Field is a low-level wrapper for custom, native, or third-party controls that do not already provide field label, description, and status UI. Use it when you need the Field shell around a control you own; use styled Astryx inputs like TextInput, Typeahead, and Select directly when they already expose label, description, and validation props.

  • Wrap custom controls, native inputs, or third-party widgets that need labeling, helper text, optional/required indicators, or validation status.
  • Always provide a label for accessibility, even if visually hidden with isLabelHidden.
  • Use inputID and descriptionID to connect the label and description to the inner control with htmlFor and aria-describedby.
  • Nest Field around styled inputs such as TextInput, Typeahead, Select, DateInput, or TextArea; those components already render their own Field shell.
  • Use the attached status variant on non-bordered controls such as sliders, switches, or checkboxes; use detached so the message does not overlap the control.
  • Set both isOptional and isRequired on the same field.
  • Hide the label without providing an alternative way for the user to understand the field purpose.

Typed props

PropType and behavior
labelstring · required
Label text for the field (always rendered for accessibility).
inputIDstring · required
ID for the input element (used for the label htmlFor attribute).
labelIDstring
ID applied to the label element itself for group accessibility.
isGroupLabelboolean · default false
Renders the label as a span for control groups (radiogroup, checkbox list).
childrenReactNode · required
The input or control to render.
isLabelHiddenboolean · default false
Visually hide the label (still accessible to screen readers).
isDisabledboolean · default false
Whether the associated input is disabled. Propagates disabled styling to the label.
descriptionstring
Description text displayed between the label and input.
descriptionIDstring
ID for the description element (use for aria-describedby on the input).
isOptionalboolean · default false
Whether the field is optional (mutually exclusive with isRequired).
isRequiredboolean · default false
Whether the field is required (mutually exclusive with isOptional).
labelIconIconType
Icon to display before the label text. See astryx docs icons for valid semantic names.
labelTooltipstring
Tooltip text to display in an info icon at the end of the label.
status{type: 'warning' | 'error' | 'success', message?: string, messageID?: string}
Status indicator with type and optional message. When message is set, displays a colored status box. messageID is for wiring aria-describedby on the input.
statusVariant'attached' | 'detached' · default 'attached'
How the status message renders relative to the input. Attached overlaps the input border; detached floats below.
widthSizeValue
Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned. Prefer this over setting width via xstyle/className/style, which only size the inner control box.
refReact.Ref<HTMLDivElement>
Ref forwarded to the root element.
xstyleStyleXStyles
StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value: not an inline style object like style={{}}.
classNamestring
CSS class name(s) appended to the root element. Prefer xstyle for StyleX deduplication.
styleReact.CSSProperties
Inline styles applied to the root element. Takes priority over StyleX inline styles.

Anatomy

Label · required

Text identifying the field. Always rendered for accessibility, optionally hidden visually.

Description · optional

Helper text between the label and input explaining what to enter.

Control slot · required

A custom, native, or third-party control that does not already render a field shell.

Status message · optional

Inline validation feedback showing error, warning, or success with a message.

Optional/Required indicator · optional

Badge next to the label showing whether the field is optional or required.

Label tooltip · optional

Info icon at the end of the label with a tooltip explaining the field.

Theming

Targets

astryx-field

Visual props: layout

astryx-field-label
astryx-field-status

Visual props: type, variant

astryx-input-status-icon

Visual props: size, status

astryx-input-clear-button
astryx-input-clear-icon

Variables

--_field-radius · private

Border radius of input fields

Default: var(--radius-element)

--_field-status-overlap · private

Amount an attached FieldStatus extends behind the lower half of the control. Set from the rendered control size.

Default: calc(var(--size-element-md) / 2)

--_input-clear-hit-inset · private

Outset of the clear (✕) button's invisible hit area, applied to a ::after overlay. 0 on a fine pointer; negative on a coarse one, which grows the 20px button to the 24px touch target without changing what is drawn.

Default: 0px

--_input-clear-hit-content · private

Whether the clear (✕) button's invisible hit overlay exists. none on a fine pointer, so no ::after is generated and hover still reaches the glyph; "" on a coarse one, where the overlay provides the 24px touch target.

Default: none

Derived properties

borderRadius

Uses --_field-radius.