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
  of={loginForm}
  path={['email']}
  render$={(field) => (
    <div>
      <label for={field.props.name}>Email</label>
      <input
        {...field.props}
        id={field.props.name}
        value={field.input.value}
        type="email"
        required
      />
      {field.errors.value && <div>{field.errors.value[0]}</div>}
    </div>
  )}
/>

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
  of={loginForm}
  path={['email']}
  render$={(field) => (
    <TextInput
      {...field.props}
      type="email"
      label="Email"
      input={field.input.value}
      errors={field.errors.value}
      required
    />
  )}
/>

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 { FieldElementProps } from '@formisch/qwik';
import { component$, type ReadonlySignal } from '@qwik.dev/core';

interface TextInputProps extends FieldElementProps {
  type: 'text' | 'email' | 'tel' | 'password' | 'url' | 'date';
  label?: string;
  placeholder?: string;
  input: ReadonlySignal<string | undefined>;
  errors: ReadonlySignal<[string, ...string[]] | null>;
  required?: boolean;
}

Component function

In the next step, add the component function to the file. We can destructure the props directly.

import type { FieldElementProps } from '@formisch/qwik';
import { component$, type ReadonlySignal } from '@qwik.dev/core';

interface TextInputProps extends FieldElementProps {
  /* ... */
}

export const TextInput = component$<TextInputProps>(
  ({ input, label, errors, ...props }) => {
    // Component JSX will go here
  }

JSX code

After that, you can add the JSX code to the return statement.

import type { FieldElementProps } from '@formisch/qwik';
import { component$, type ReadonlySignal } from '@qwik.dev/core';

interface TextInputProps extends FieldElementProps {
  /* ... */
}

export const TextInput = component$<TextInputProps>(
  ({ input, label, errors, ...props }) => {
    const { name, required } = props;
    return (
      <div>
        {label && (
          <label for={name}>
            {label} {required && <span>*</span>}
          </label>
        )}
        <input
          {...props}
          id={name}