Throw form errors

Modular Forms has built-in error handling with the FormError class to easily display individual errors when submitting a form.

Throw general error

General errors are errors that affect the entire form and can be displayed via the response state in the UI, e.g. above the submit button.

Client-side errors

When processing the form client-side via the onSubmit$ event listener, any error thrown is automatically caught and added to the response state.

To throw custom errors to be displayed to the user, you can use our FormError class. It inherits from the default Error class and can contain a general error message as well as error messages for individual fields. More about this in a moment.

import { $, component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import type { InitialValues, FormError } from '@modular-forms/qwik';
import { formAction$, valiForm$ } from '@modular-forms/qwik';
import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(
    v.string(),
    v.nonEmpty('Please enter your email.'),
    v.email('The email address is badly formatted.')
  ),
  password: v.pipe(
    v.string(),
    v.nonEmpty('Please enter your password.'),
    v.minLength(8, 'You password must have 8 characters or more.')
  ),
});

type LoginForm = v.InferInput<typeof LoginSchema>;

export const useFormLoader = routeLoader$<InitialValues<LoginForm>>(() => ({
  email: '',
  password: '',
}));

export default component$(() => {
  const [loginForm, { Form, Field }] = useForm<LoginForm>({
    loader: useFormLoader(),
    validate: valiForm$(LoginSchema),
  });

  const handleSubmit: SubmitHandler<LoginForm> = $((values, event) => {
    if (error) {
      throw new FormError<LoginForm>('An error has occurred.');
    }
  });

  return (
    <Form onSubmit$={handleSubmit}>
      <div></div>
      <div>{loginForm.response.message}</div>
      <button type="submit">Login</button>
    </Form>
  );
});

Server-side errors

For server-side errors, we are a bit more cautious, as secret information might be preserved here and therefore only output a generic error message for all errors that are not an instance of our FormError class. So, to intentionally output custom errors to the user, our FormError class must be used.

export const useFormAction = formAction$<LoginForm>((values) => {
  if (error) {
    throw new FormError<LoginForm>('An error has occurred.');
  }
}, valiForm$(LoginSchema));

Throw field error

Field errors are errors that are assigned to a specific field and added to its state. In addition to a general error message, you can also add an error message to specific fields using the second argument of the FormError class constructor.

if (error) {
  throw new FormError<LoginForm>('An error has occurred.', {
    email: 'This email has been blacklisted.',
  });
}

Field errors only

To not display a general error message and instead display only errors of individual fields, you can simply omit the general message.

if (error) {
  throw new FormError<LoginForm>({
    email: 'This email has been blacklisted.',
  });
}