Form Validation In Remix Using Yup
Tags: remix, react, javascript, video
Published: 2022-01-15 21:43:12

### Overview
Simple application showing how to implement form validation in your [Remix](https://remix.run/docs/en/v1) application and [Yup](https://www.npmjs.com/package/yup)

I use Yup with React-Hook-From and I was looking to see if I could use it with Remix... I got it working and I thought it would be interesting to share in an video

>[Remix](https://remix.run/docs/en/v1) is a full stack web framework that lets you focus on the user interface and work back through web fundamentals to deliver a fast, slick, and resilient user experience.

### The Video
{% youtube mwqA-5bXP5w %}

### Source Code
```react
import { Form, Link, ActionFunction, redirect, useActionData } from "remix";
import * as Yup from "yup";
import { addProject } from "./dataService";

/**
 *
 * @param formData
 * @returns
 */
const validateForm = async (formData: FormData) => {

  const getValidationErrors = (err: any) => {
    const validationErrors = {} as any;

    err.inner.forEach((error: any) => {
      if (error.path) {
        validationErrors[error.path] = error.message;
      }
    });

    return validationErrors;
  };

  // convert form into JSON object
  const formJSON: { [key: string]: any } = {};
  for (var key of formData.keys()) {
    formJSON[key] = formData.get(key);
  }

  // Yup schema for the object that I am trying to validate
  const projectSchema = Yup.object({
    name: Yup.string().required("Name is a required field").nullable(),
    email: Yup.string()
      .email("This is not a valid email")
      .required("Email is a required field")
      .nullable(),
    description: Yup.string()
      .required("Description is a required field")
      .nullable(),
    createdOn: Yup.date().default(() => new Date()),
  });

  // validate the object and throw error if not valid
  try {
    const project = await projectSchema.validate(formJSON, { abortEarly: false });
    return project;
  } catch (error) {
    throw getValidationErrors(error);
  }
};

/**
 * 
 * @param param0 
 * @returns 
 */
export const action: ActionFunction = async ({ request }) => {
  const formData = await request.formData();

  try {
    // validate
    const project = await validateForm(formData);
    //save
    const newProject = await addProject(project);
    return redirect(`/projects/${newProject.id}`);
  } catch (errors) {
    return { errors };
  }
};

export default function NewProject() {
  // data returned after submitting the form
  const actionData = useActionData();

  return (
    <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
    <h2>Remix Form Validation With Yup</h2>
    <Form method="post" action="/new-item" noValidate={true}>
      <p>
        <label>
          Name:{" "}
          <input
            name="name"
            type="text"
            style={{ borderColor: actionData?.errors["name"] && "red" }}
          />
        </label>
        <div>{actionData?.errors["name"]}</div>
      </p>
      <p>
        <label>
          Email:{" "}
          <input
            name="email"
            type="email"
            style={{ borderColor: actionData?.errors["email"] && "red" }}
          />
        </label>
        <div>{actionData?.errors["email"]}</div>
      </p>
      <p>
        <label>
          Description:
          <br />
          <textarea
            name="description"
            style={{ borderColor: actionData?.errors["description"] && "red" }}
          />
        </label>
        <div>{actionData?.errors["description"]}</div>
      </p>
      <p>
        <button type="submit">CREATE</button>
        <Link to={"/"} style={{ marginLeft: 12 }}>
          <button type="submit">CANCEL</button>
        </Link>
      </p>
      <pre>{JSON.stringify(actionData?.errors, null, 2)}</pre>
    </Form>
    </div>
  );
}
```