Skip to content
Components, switch product

Text input

<gui-textinput> is a single-line text field. It renders a native <input type="text"> with its label, hint and errors, and keeps them connected for assistive technology.

Code of the example above
TextInputBasic.tsx
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextInputBasic() {
return <GuiTextinput label="Full name" />;
}

The element’s entry point is @golemui/gui-components/textinput, which registers <gui-textinput> and nothing else. To install the package and load the stylesheet, see Installation.

The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.

EmailField.tsx
import { useState } from 'react';
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function EmailField() {
const [email, setEmail] = useState('');
return (
<GuiTextinput
label="Email"
name="email"
value={email}
onGuiChange={(event) => setEmail(event.detail.value ?? '')}
/>
);
}

Properties

Property Type Default Description
localeId attribute locale-id string 'en' BCP 47 locale for formatting and parsing, such as en-US or es.
value string – The text.
icon string – Icon class name shown inside the control, for example from an icon font.
placeholder string – Text shown while the control is empty.
autocomplete string – The autocomplete hint passed to the inner native control.
uid string – The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration.

Form control properties

Every field has these, from the base class of the form controls.

Property Type Default Description
name string – The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute.
label string – The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds.
hint string – Help text shown under the label and announced as the control's description.
errors string[] [] Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false.
touched boolean – Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true.
required boolean false The control needs a value: it is marked as required and, with a name, blocks submission.
readOnly attribute readonly boolean – The value can be read and focused but not changed.
disabled boolean – Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's.
form read-only, no attribute HTMLFormElement | null – The <form> the element belongs to, if any.
validity read-only, no attribute ValidityState – The element's validity, like a native control's.
validationMessage read-only, no attribute string – The message the browser shows for an invalid value.

Methods

Method Description
checkValidity(): boolean –
reportValidity(): boolean –

Events

Every event bubbles and is composed, so you can listen on any ancestor.

Event Description
gui-input The user changed the value. detail.value is the new value.
gui-change The user committed the text, on blur or Enter. detail.value is the value.
gui-blur Focus left the control.

placeholder shows while the field is empty. It is not a label: screen readers may skip it, and it disappears as soon as the user types, so always set label too.

Code of the example above
TextInputPlaceholder.tsx
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextInputPlaceholder() {
return <GuiTextinput label="City" placeholder="For example, Lisbon" />;
}

hint shows under the label. Screen readers read it as the field’s description, after its name.

Code of the example above
TextInputHint.tsx
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextInputHint() {
return (
<GuiTextinput
label="Username"
hint="Letters and numbers only. You can change it later."
/>
);
}

icon takes the class names of an icon, from any icon font or your own CSS, and shows it at the start of the field. The icon is decorative and hidden from assistive technology. See Icons for the ways to draw it.

Code of the example above
TextInputIcon.tsx
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
// Any icon font works: icon sets these classes on the icon element.
// This one draws a Material Icons ligature.
const iconStyles = `
.icon-search::before {
content: 'search';
font-family: 'Material Icons';
}
`;
export function TextInputIcon() {
return (
<>
<GuiTextinput
label="Search"
icon="icon-search"
placeholder="Search the docs"
/>
<style>{iconStyles}</style>
</>
);
}

A disabled field can’t be focused or changed, and a form doesn’t submit it. A readonly field can be focused, selected and copied, and a form submits its value. A disabled <fieldset> disables every element inside it.

Code of the example above
TextInputDisabledReadonly.tsx
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextInputDisabledReadonly() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiTextinput label="Plan" value="Team" disabled />
</div>
<div className="gui-grid__cell">
<GuiTextinput label="Account ID" value="ACME-2041" readOnly />
</div>
</div>
);
}

errors takes a list of messages, usually from your own validation. As an attribute it is a JSON array. Errors show as soon as you set them, and the field is marked invalid.

Code of the example above
TextInputErrors.tsx
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
const errors = ['Enter a complete email address, like ada@example.com.'];
export function TextInputErrors() {
return <GuiTextinput label="Email" value="ada@" errors={errors} />;
}

To validate as the user works, set the errors property from a gui-change listener. An empty list clears them. If you validate on interaction and set errors before the user has touched the field, set touched to false until they do: it holds the errors back.

Code of the example above
TextInputValidation.tsx
import { useState } from 'react';
import { GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextInputValidation() {
const [errors, setErrors] = useState<string[]>([]);
return (
<GuiTextinput
id="signup-email"
label="Email"
hint="Leave the field to check it."
errors={errors}
onGuiChange={(event) => {
const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(
event.detail.value ?? '',
);
setErrors(
valid
? []
: ['Enter a complete email address, like ada@example.com.'],
);
}}
/>
);
}

With a name, <gui-textinput> submits its text under that name, and required blocks an empty submit. Submit the empty field below, then fill it in and submit again.

Code of the example above
TextInputNativeForm.tsx
import { useState, type SubmitEvent } from 'react';
import { GuiButton, GuiTextinput } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextInputNativeForm() {
const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget));
setResult(`Submitted ${JSON.stringify(data)}`);
};
return (
<form id="newsletter" onSubmit={onSubmit}>
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell gui-grid__cell--span-3">
<GuiTextinput label="Email" name="email" required />
</div>
<div className="gui-grid__cell">
<GuiButton type="submit" label="Subscribe" />
</div>
</div>
<p id="newsletter-result" role="status">
{result}
</p>
</form>
);
}

How the elements submit, validate and reset is in Native forms.

<gui-textinput> renders into the light DOM, so you can style every part with plain CSS. These are the parts and their public classes:

gui-textinput
<gui-textinput class="gui-field">
<label class="gui-label" for="email" id="email_label">
<span class="gui-label__text">Email<span aria-hidden="true"> *</span></span>
<div class="gui-widget-hint" id="email_hint" aria-hidden="true">
We never share it.
</div>
</label>
<div class="gui-widget">
<input
type="text"
id="email"
class="gui-widget-input gui-textinput--icon"
aria-describedby="email_hint"
aria-required="true"
aria-invalid="true"
aria-errormessage="email_errors"
/>
<span
class="gui-widget-icon icon-mail"
data-icon="icon-mail"
aria-hidden="true"
></span>
</div>
<div class="gui-validator" id="email_errors" role="alert">
<div class="gui-validator__error">Enter a complete email address.</div>
</div>
</gui-textinput>
ClassPart
gui-fieldThe element itself. Lines its parts up with the fields next to it in a gui-grid row.
gui-labelThe label, with the text and the hint.
gui-label__textThe label text and the required marker.
gui-widget-hintThe hint.
gui-widgetThe box around the control.
gui-widget-inputThe <input>, the part that looks like a field.
gui-textinput--iconOn the <input> when there is an icon: it makes room for it.
gui-widget-iconThe icon, with your icon classes.
gui-validatorThe errors container. It is always there, so the alert role announces new errors.
gui-validator__errorOne error message.

The ids derive from uid, or from a generated id such as gui-textinput-3 when you don’t set one. Select by class or by tag, not by id.

The field reads these design tokens. Set them on :root to change every field, or on any element to change the fields inside it:

TokenWhat it styles
--gui-widget-sizeThe height of the field.
--gui-radius-mdThe corner radius.
--gui-bg-surfaceThe field’s background.
--gui-border-defaultThe border.
--gui-text-defaultThe text, the label and the icon.
--gui-font-familyThe font.
--gui-font-smThe text size of the label and the field.
--gui-font-xsThe text size of the hint and the errors.
--gui-text-secondaryThe hint.
--gui-border-focusThe border while focused.
--gui-shadow-focusThe focus ring.
--gui-intent-errorThe border and the messages when there are errors.
--gui-shadow-focus-errorThe focus ring when there are errors.
--gui-bg-disabledThe background when disabled.
--gui-text-disabledThe text when disabled.
--gui-space-1The space between the label, hint and errors.
styles.css
/* Taller, rounder fields everywhere. */
:root {
--gui-widget-size: 48px;
--gui-radius-md: 12px;
}
/* A brand focus color in one form only. */
.checkout {
--gui-border-focus: #d96c3f;
}

To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.

  • Name. The label names the field. Without one the field has no accessible name.
  • Description. The hint is the field’s description, through aria-describedby.
  • Errors. While errors show, the input has aria-invalid="true" and points at them with aria-errormessage, and screen readers announce new errors as they appear.
  • Required. required sets aria-required; the visual marker is hidden from screen readers.
KeyAction
TabMoves focus to the field, and away from it.
EnterCommits the value, as leaving the field does, and fires gui-change.
Every editing keyWorks as in any native text input.

What every element shares, such as translating the browser messages, is in Accessibility.