Skip to content

TextField

A field of a label, an input, a description and an error.

Solidtype_heroui.Text_field is Kobalte’s TextField.Root, with the classes HeroUI’s own textFieldVariants gives, and the parts inside it as Kobalte’s: its Label names its Input or TextArea, its Description describes it, and its FieldError shows while it is invalid. It keeps its value, controlled or its own, and declares its state as React Aria writes it.

text_field_usage.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<Text_field class_="w-full max-w-64" name="email">
<Label>"Email"</Label>
<Input type_="email" placeholder="Enter your email" />
</Text_field>
Choose a unique username for your account
We'll never share your email with anyone
text_field_description.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<div class_="flex w-full max-w-64 flex-col gap-4">
<Text_field name="username">
<Label>"Username"</Label>
<Input placeholder="Enter username" />
<Description>"Choose a unique username for your account"</Description>
</Text_field>
<Text_field name="email">
<Label>"Email"</Label>
<Input type_="email" placeholder="Enter your email" />
<Description>"We'll never share your email with anyone"</Description>
</Text_field>
</div>

A required field marks its label, as HeroUI’s styles mark it.

Required field
text_field_required.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<div class_="flex w-full max-w-64 flex-col gap-4">
<Text_field is_required=true name="name">
<Label>"Name"</Label> <Input placeholder="Enter your name" />
</Text_field>
<Text_field is_required=true name="email">
<Label>"Email"</Label>
<Input type_="email" placeholder="Enter your email" />
<Description>"Required field"</Description>
</Text_field>
</div>
This field is disabled
text_field_disabled.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<div class_="flex w-full max-w-64 flex-col gap-4">
<Text_field is_disabled=true name="disabled">
<Label>"Disabled field"</Label>
<Input placeholder="Cannot edit" />
<Description>"This field is disabled"</Description>
</Text_field>
<Text_field is_disabled=true name="email" default_value="ada@example.org">
<Label>"Email"</Label> <Input type_="email" />
</Text_field>
</div>
text_field_full_width.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<div class_="w-[400px] space-y-4">
<Text_field full_width=true name="full-width">
<Label>"Full width"</Label>
<Input full_width=true placeholder="Takes the container's width" />
</Text_field>
</div>

is_invalid shows the field’s Field_error, which its input is then described by, and HeroUI’s styles hide its description.

Choose a unique username for your profile.
Minimum 20 characters (0/20).
text_field_validation.mlx
module S = Solidtype.Solid
module Jsx = Solidtype.Jsx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
let username, set_username = S.create_signal "" in
let bio, set_bio = S.create_signal "" in
let short name n () =
let length = String.length (S.get name) in
length > 0 && length < n
in
<div class_="flex w-full max-w-64 flex-col gap-4">
<Text_field
is_required=true
is_invalid=(short username 3)
name="username"
value=(fun () -> S.get username)
on_change=(S.set set_username)>
<Label>"Username"</Label>
<Input placeholder="jane_doe" />
<Field_error>"Username must be at least 3 characters."</Field_error>
<Description>"Choose a unique username for your profile."</Description>
</Text_field>
<Text_field
is_required=true
is_invalid=(short bio 20)
name="bio"
value=(fun () -> S.get bio)
on_change=(S.set set_bio)>
<Label>"Bio"</Label>
<Text_area placeholder="Tell us about yourself..." />
<Field_error>"Bio must contain at least 20 characters."</Field_error>
<Description>
(fun () ->
Jsx.string
("Minimum 20 characters ("
^ string_of_int (String.length (S.get bio))
^ "/20)."))
</Description>
</Text_field>
</div>
Characters: 0
text_field_controlled.mlx
module S = Solidtype.Solid
module Jsx = Solidtype.Jsx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
let name, set_name = S.create_signal "" in
<div class_="flex w-full max-w-64 flex-col gap-4">
<Text_field
name="name" value=(fun () -> S.get name) on_change=(S.set set_name)>
<Label>"Display name"</Label>
<Input placeholder="Jane" />
<Description>
(fun () ->
Jsx.string
("Characters: " ^ string_of_int (String.length (S.get name))))
</Description>
</Text_field>
</div>
Up to 500 characters
text_field_text_area.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<Text_field class_="w-full max-w-64" name="message">
<Label>"Message"</Label>
<Text_area placeholder="Write a message" />
<Description>"Up to 500 characters"</Description>
</Text_field>
text_field_input_types.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<div class_="flex w-full max-w-64 flex-col gap-4">
<Text_field name="text">
<Label>"Text"</Label> <Input type_="text" placeholder="Enter text" />
</Text_field>
<Text_field name="email">
<Label>"Email"</Label>
<Input type_="email" placeholder="name@example.org" />
</Text_field>
<Text_field name="password">
<Label>"Password"</Label>
<Input type_="password" placeholder="Enter password" />
</Text_field>
<Text_field name="number">
<Label>"Number"</Label> <Input type_="number" placeholder="0" />
</Text_field>
<Text_field name="url">
<Label>"URL"</Label>
<Input type_="url" placeholder="https://example.org" />
</Text_field>
</div>

React Aria’s validate, validationErrors and native validation on submit, and HeroUI’s In Surface example’s surface-aware variant, are not ported yet: a field is invalid as is_invalid says.

class_ is added after HeroUI’s classes, on the field and on each part.

text_field_tailwind.mlx
module Description = Solidtype_heroui.Description
module Field_error = Solidtype_heroui.Field_error
module Input = Solidtype_heroui.Input
module Label = Solidtype_heroui.Label
module Text_area = Solidtype_heroui.Text_area
module Text_field = Solidtype_heroui.Text_field
let make () =
<Text_field class_="w-full max-w-64 gap-2" name="handle">
<Label class_="text-xs font-medium tracking-wide uppercase">"Handle"</Label>
<Input class_="rounded-full" placeholder="@ada" />
</Text_field>
  • .textfield, and .textfield--full-width
  • Invalid: [data-invalid="true"], which hides the description
  • Disabled: [data-disabled="true"]
  • Read-only: [data-readonly="true"]
  • Required: [data-required="true"], which marks the label

Kobalte writes its own state on each part too, present and empty, which HeroUI’s styles pass over, and gives the field the group role.

Each prop beside the name HeroUI’s React component gives it.

React solidtype Type Default Description
variant variant [ `primary | `secondary ] Its input’s variant, where the input sets none
fullWidth full_width bool false Whether it takes its container’s width
value value string Its value, controlled
defaultValue default_value string Its value at first, kept by itself
onChange on_change string -> unit Told each change of its value
isRequired is_required bool false Whether it is required
isInvalid is_invalid bool false Whether it is invalid, its error shown
isDisabled is_disabled bool false Whether it is disabled
isReadOnly is_read_only bool false Whether it is read-only
name name string Its input’s name in a form
className class_ string Added after HeroUI’s classes
onBlur on_blur Dom.focusEvent -> unit Called once focus leaves it for an element outside it
children children elements Its label, input, description and error
validate, validationErrors, validationBehavior — Not yet

Original HeroUI documentation: TextField.

The same HeroUI TextField twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of the two inputs: a marked row is one they disagree on, and a dimmed row is a framework’s own, which no style reads. Hover, focus and type in them to see the state each sets.

solidtype — OCaml, on Solid and Kobalte
We'll never share it.
React — TypeScript, on React Aria
attributesolidtypeReact