Input components
To make your code more readable, we recommend that you develop your own input components if you are not using a prebuilt UI library. There you can encapsulate logic to display error messages, for example.
If you're already a bit more experienced, you can use the input components we developed for our playground as a starting point. You can find the code in our GitHub repository here.
Why input components?
Currently, your fields might look something like this:
<Field name="email" validate={…}> {(field, props) => ( <div> <label for={field.name}>Email</label> <input {...props} id={field.name} value={field.value} type="email" required /> {field.error && <div>{field.error}</div>} </div> )} </Field>
If CSS and a few more functionalities are added here, the code quickly becomes confusing. In addition, you have to rewrite the same code for almost every form field.
Our goal is to develop a TextInput
component so that the code ends up looking like this:
<Field name="email" validate={…}> {(field, props) => ( <TextInput {...props} type="email" label="Email" value={field.value} error={field.error} required /> )} </Field>
Create an input component
In the first step, you create a new file for the TextInput
component and, if you use TypeScript, define its properties.
import { type QRL } from '@builder.io/qwik'; type TextInputProps = { name: string; type: 'text' | 'email' | 'tel' | 'password' | 'url' | 'date'; label?: string; placeholder?: string; value: string | undefined; error: string; required?: boolean; ref: QRL<(element: HTMLInputElement) => void>; onInput$: (event: Event, element: HTMLInputElement) => void; onChange$: (event: Event, element: HTMLInputElement) => void; onBlur$: (event: Event, element: HTMLInputElement) => void; };
Component function
In the next step, add the component$
function to the file and destructure the properties of the HTML <input />
element from the rest.
import { component$ } from '@builder.io/qwik'; type TextInputProps = { … }; export const TextInput = component$( ({ label, error, ...props }: TextInputProps) => { const { name, required } = props; } );
JSX code
After that, next you can add the JSX code to the return statement.
import { component$ } from '@builder.io/qwik'; type TextInputProps = { … }; export const TextInput = component$( ({ label, error, ...props }: TextInputProps) => { const { name, required } = props; return ( <div> {label && ( <label for={name}> {label} {required && <span>*</span>} </label> )} <input {...props} id={name} aria-invalid={!!error} aria-errormessage={`${name}-error`} /> {error && <div id={`${name}-error`}>{error}</div>} </div> ); } );
Next steps
You can now build on this code and add CSS, for example. You can also follow the procedure to create other components such as Checkbox
, Slider
, Select
and FileInput
.
Final code
Below is an overview of the entire code of the TextInput
component.
import { component$, type QRL } from '@builder.io/qwik'; type TextInputProps = { name: string; type: 'text' | 'email' | 'tel' | 'password' | 'url' | 'date'; label?: string; placeholder?: string; value: string | undefined; error: string; required?: boolean; ref: QRL<(element: HTMLInputElement) => void>; onInput$: (event: Event, element: HTMLInputElement) => void; onChange$: (event: Event, element: HTMLInputElement) => void; onBlur$: (event: Event, element: HTMLInputElement) => void; }; export const TextInput = component$( ({ label, error, ...props }: TextInputProps) => { const { name, required } = props; return ( <div> {label && ( <label for={name}> {label} {required && <span>*</span>} </label> )} <input {...props} id={name} aria-invalid={!!error} aria-errormessage={`${name}-error`} /> {error && <div id={`${name}-error`}>{error}</div>} </div> ); } );