Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsForm Blocks

Field

A text input field with a label, helper text, FieldDropdown prefix, Button suffix, leading/trailing icon buttons, a clearable button, and a character counter. Supports all intent states and full RTL layout.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { Field, FieldDropdown, FieldDropdownItem } from "@tessinaui/ui";
import { Button } from "@tessinaui/ui";
<Field label="Email" placeholder="you@example.com" />

Three label layout variants are available via labelPosition:

  • "outside-top" (default) — label above the field
  • "inside" — label inside the container above the placeholder text
  • "outside-left" — label to the left of the field (horizontal layout)

Showcase

All intents, sizes, and feature combinations:

API Reference

Props

PropTypeDefaultDescription
labelstring—Label text
requiredbooleanfalseShows a red * after the label and sets aria-required
labelPosition"outside-top" | "inside" | "outside-left""outside-top"Label layout variant. inside renders the label inside the container above the input. outside-left creates a horizontal label + field layout
infoTextstring—Small info text shown top-right in the label row (or next to label for outside-left)
intent"none" | "error" | "warning" | "success" | "info""none"Controls border color, focus ring color, and supporting text color/icon
size"xs" | "sm" | "md" | "lg" | "xl""md"Controls height, padding (proportional to size), and text/icon size
rounded"full" | "lg" | "md" | "sm" | "none""full"Border radius
leadingIconReact.ReactNode—Icon rendered inside the container on the leading side (as a ghost IconButton)
leadingIconLabelstring""Accessible label for the leading icon (screen readers)
leadingIconOnClick() => void—Makes the leading icon interactive when provided
trailingIconReact.ReactNode—Icon rendered inside the container on the trailing side
trailingIconLabelstring""Accessible label for the trailing icon
trailingIconOnClick() => void—Makes the trailing icon interactive when provided
clearablebooleanfalseShows an × IconButton when the field has a value. Visible alongside trailingIcon
onClear() => void—Called when the clear button is clicked
prefixReact.ReactNode—Slot to the left of the input container — use <FieldDropdown> for the country selector pattern
suffixReact.ReactNode—Slot to the right of the input container — use <Button> with matching size and rounded
supportingTextstring—Text below the input. Color and icon follow intent
showCharacterCounterbooleanfalseShows current / maxLength counter. Requires maxLength
dir"ltr" | "rtl"—Text direction. RTL flips the full layout
wrapperClassNamestring—Extra className for the outer wrapper <div>
containerClassNamestring—Extra className for the bordered container <div>
disabledbooleanfalseDisables the input and mutes all styles
maxLengthnumber—Native maxLength for the <input>. Used by character counter
placeholderstring—Native placeholder text
value / defaultValuestring—Controlled / uncontrolled value
onChangeChangeEventHandler—Native onChange handler

All other native <input> attributes (type, name, autoComplete, etc.) are forwarded.

With Prefix & Suffix

Use <FieldDropdown> for the prefix (country selector, currency picker) and <Button> for the suffix. Pass the same size and rounded values to all three so they share identical border radius and height:

import { Field, FieldDropdown } from "@tessinaui/ui";
import { Button } from "@tessinaui/ui";

<Field
  label="Phone"
  placeholder="Enter number"
  rounded="full"
  size="md"
  prefix={
    <FieldDropdown
      flag="si"
      text="SLO(+386)"
      size="md"
      rounded="full"
    />
  }
  suffix={
    <Button size="sm" rounded="full">
      Verify
    </Button>
  }
/>

FieldDropdown props

PropTypeDefaultDescription
flagstring—ISO 3166-1 alpha-2 country code (e.g. "si" for Slovenia). Loads from flagcdn.com
textstring—Text label shown next to the flag (e.g. "SLO(+386)")
showChevronbooleantrueWhether to show the ⌄ chevron. When false, the button is non-interactive (pointer-events-none)
childrenReact.ReactNode—Dropdown items (use <FieldDropdownItem>). When provided with showChevron={true}, clicking opens a Radix dropdown
roundedsame as Field"full"Must match the parent Field's rounded prop
sizesame as Field"md"Must match the parent Field's size prop
onClickMouseEventHandler—Click handler when no children dropdown is used

Controlled Usage

const [value, setValue] = useState("");

<Field
  label="Search"
  value={value}
  onChange={(e) => setValue(e.target.value)}
  clearable
  onClear={() => setValue("")}
  showCharacterCounter
  maxLength={100}
/>

Label Variants

{/* Inside — label floats above input text inside the container */}
<Field labelPosition="inside" label="Email" placeholder="you@example.com" />

{/* Outside left — horizontal layout, label column on the leading side */}
<Field labelPosition="outside-left" label="Email" placeholder="you@example.com" />

RTL

Pass dir="rtl" to flip the full layout including the label row, input icons, prefix/suffix, and supporting text. Works with all labelPosition variants:

<Field dir="rtl" label="بحث" placeholder="نص العنصر النائب" leadingIcon={<Search />} />
<Field dir="rtl" labelPosition="outside-left" label="البريد" placeholder="you@example.com" />

Accessibility

  • The <input> is associated with its <label> via a generated (or provided) id/htmlFor pair
  • aria-required is set when required={true}
  • aria-invalid is set when intent="error"
  • The clear button has aria-label="Clear" and is excluded from tab order (tabIndex={-1})
  • Touch target for the clear button: ≥ 44×44 px
  • Focus ring appears on the container (not the inner input) via focus-within:ring-*
  • Color contrast: all intent states meet WCAG 2.1 AA

Label

A foundational form label primitive. Five sizes, five intents, three tones, three weights, required/optional indicators, leading/trailing icons, optional description, and RTL support.

Fieldset

Groups related form controls under a shared legend, description, and helper text — built on Base UI's Fieldset primitive with four variants, five sizes, intent accents, and full RTL support.

On this page

PlaygroundInstallationUsageShowcaseAPI ReferencePropsWith Prefix & SuffixFieldDropdown propsControlled UsageLabel VariantsRTLAccessibility