@Component()class MyComponent extends StatefulComponent { @State() field = new Field({ required: true }) @State() email = '' @State() invalid = false validate() { this.invalid = !this.email.includes('@') } render() { return ( <Field class="morphos-field" required invalid={this.invalid}> <FieldLabel field={this.field} class="morphos-field-label">Email address</FieldLabel> <FieldDescription field={this.field} class="morphos-field-description"> We will never share your email with anyone. </FieldDescription> <FieldControl class="morphos-field-control"> <Input id={this.field.fieldId} type="email" class="morphos-input" value={this.email} onInput={(value) => (this.email = value)} onBlur={() => this.validate()} aria-describedby={this.field.descriptionId} invalid={this.invalid} /> </FieldControl> <FieldError field={this.field} class="morphos-field-error"> Please enter a valid email address. </FieldError> </Field> ) }}
Field is a regular stateful component rendered directly with props (required, invalid, disabled, class, ...). The separate new Field(...) instance kept in @State() is what FieldLabel, FieldDescription, and FieldError read their field.fieldId / field.descriptionId / field.errorId / field.invalid from — construct it with matching options so the IDs and state line up with what you pass to the rendered <Field>.
Field auto-generates stable IDs for the description and error elements (descriptionId, errorId) and resolves fieldId from its own id prop or an auto-generated fallback. FieldLabel renders <label htmlFor={field.fieldId}>, so give your input id={field.fieldId} for the label to focus it on click. FieldError uses role="alert" and is only rendered when field.invalid is true, so the error is announced to screen readers as soon as it appears.
You do not need to use FieldControl if you do not need a wrapper div — it is provided as a styling convenience only, and its field prop (declared in the types) is not read by its render().
Field, FieldDescription, and FieldError do not expose a labelId. Wire aria-describedby={field.descriptionId} and the invalid/required props manually onto your input — there is no aria-errormessage prop on @morphos/inputs form controls.