<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Gift Uhiene - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Gift Uhiene - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 09 Jun 2026 04:37:55 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/Giftea/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Validate Forms with Zod and React-Hook-Form ]]>
                </title>
                <description>
                    <![CDATA[ Forms allow you to collect user data on your websites and apps. And validation is essential to guarantee type safety and the proper format for collected data. You can perform validation on both the client and server side of the application.  This is ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-form-validation-zod-react-hook-form/</link>
                <guid isPermaLink="false">66bb580c0da5b03e481107d0</guid>
                
                    <category>
                        <![CDATA[ forms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ zod ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gift Uhiene ]]>
                </dc:creator>
                <pubDate>Wed, 17 Jan 2024 21:58:56 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/Frame-7.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Forms allow you to collect user data on your websites and apps. And validation is essential to guarantee type safety and the proper format for collected data. You can perform validation on both the client and server side of the application. </p>
<p>This is where Zod and React-Hook-Form come in as a dynamic duo, ready to take your forms to the next level.</p>
<p><a target="_blank" href="https://zod.dev/">Zod</a> is a validation library that provides a concise and expressive syntax for defining data schemas, making it an excellent choice for validating form data. </p>
<p>On the other hand, <a target="_blank" href="https://react-hook-form.com/">React-Hook-Form</a> is a lightweight form library for React that embraces uncontrolled components and simplifies form-building with its intuitive hooks-based API.</p>
<p>In this tutorial, you will learn how to build a type-safe form using React-Hook-Form for form management and Zod for both client-side and server-side validation.</p>
<h3 id="heading-heres-what-well-cover">Here's what we'll cover:</h3>
<ol>
<li><a class="post-section-overview" href="#heading-getting-started">Getting Started</a></li>
<li><a class="post-section-overview" href="#heading-how-to-define-form-types">How to Define Form Types</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-form-with-react-hook-form">How to Create a Form with react-hook-form</a></li>
<li><a class="post-section-overview" href="#heading-how-to-integrate-zod-for-schema-validation">How to Integrate Zod for Schema Validation</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-server-side-errors">How to Handle Server-Side Errors</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-getting-started">Getting Started</h2>
<p>To get started, clone the starter boilerplate for the project. Open up your terminal and run this command:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> --branch starter https://github.com/Giftea/zod-rhf-fcc.git
</code></pre>
<p>You can find the final version on GitHub <a target="_blank" href="https://github.com/Giftea/zod-rhf-fcc">here</a>.</p>
<p>Once you've got the boilerplate on your local machine, run the following commands to install dependencies and start the project:</p>
<pre><code class="lang-bash">npm install
npm run dev
</code></pre>
<p>Point your browser to <a target="_blank" href="http://localhost:3000">http://localhost:3000</a>, and you'll be greeted by the starting page of our project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-16-at-15.21.10.png" alt="Image" width="600" height="400" loading="lazy">
<em>localhost</em></p>
<h2 id="heading-how-to-define-form-types">How to Define Form Types</h2>
<p>The <code>/types.ts</code> file will contain the types and schemas related to our form fields and their validation. Update the <code>/types.ts</code> file with the code below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FieldError, UseFormRegister } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hook-form"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> FormData = {
    email: <span class="hljs-built_in">string</span>;
    githubUrl: <span class="hljs-built_in">string</span>;
    yearsOfExperience: <span class="hljs-built_in">number</span>;
    password: <span class="hljs-built_in">string</span>;
    confirmPassword: <span class="hljs-built_in">string</span>;
  };

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> FormFieldProps = {
    <span class="hljs-keyword">type</span>: <span class="hljs-built_in">string</span>;
    placeholder: <span class="hljs-built_in">string</span>;
    name: ValidFieldNames;
    register: UseFormRegister&lt;FormData&gt;;
    error: FieldError | <span class="hljs-literal">undefined</span>;
    valueAsNumber?: <span class="hljs-built_in">boolean</span>;
  };


  <span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> ValidFieldNames =
  | <span class="hljs-string">"email"</span>
  | <span class="hljs-string">"githubUrl"</span>
  | <span class="hljs-string">"yearsOfExperience"</span>
  | <span class="hljs-string">"password"</span>
  | <span class="hljs-string">"confirmPassword"</span>;
</code></pre>
<p><code>FormData</code> represents the structure of the data expected in the form.</p>
<p><code>FormFieldProps</code> defines the properties expected by the form field component (which we will build later on). It includes:</p>
<ul>
<li><code>type</code>: The type of the input field (for example, text, password).</li>
<li><code>placeholder</code>: Placeholder text for the input field.</li>
<li><code>name</code>: The name of the field, which corresponds to one of the valid field names defined in the <code>ValidFieldNames</code> type.</li>
<li><code>register</code>: A function from <code>react-hook-form</code> (<code>UseFormRegister&lt;FormData&gt;</code>) used to register the input field with the form.</li>
<li><code>error</code>: Represents any validation error associated with the field. It can be <code>undefined</code> if there are no errors.</li>
<li><code>valueAsNumber</code> (optional): A boolean flag indicating whether the field value should be treated as a number. Defaults to <code>undefined</code>.</li>
</ul>
<p><code>ValidFieldNames</code> is a union type that enumerates the valid field names for the form. These correspond to the fields defined in the <code>FormData</code> type.</p>
<h2 id="heading-how-to-create-a-form-with-react-hook-form">How to Create a Form with React-Hook-Form</h2>
<p>Now that we have defined the types for the form, let's create a reusable form field component and the form component.</p>
<h3 id="heading-create-a-reusable-form-field-component">Create a Reusable Form Field Component</h3>
<p>Let's create a reusable <code>FormField</code> component that handles rendering an input element, registering it with the form using <code>react-hook-form</code>, and displaying a validation error message when necessary.</p>
<p>Head on to the <code>/app/components/FormField.tsx</code> file and update the component:</p>
<pre><code class="lang-tsx">import { FormFieldProps } from "@/types";

const FormField: React.FC&lt;FormFieldProps&gt; = ({
  type,
  placeholder,
  name,
  register,
  error,
  valueAsNumber,
}) =&gt; (
  &lt;&gt;
    &lt;input
      type={type}
      placeholder={placeholder}
      {...register(name, { valueAsNumber })}
    /&gt;
    {error &amp;&amp; &lt;span className="error-message"&gt;{error.message}&lt;/span&gt;}
  &lt;/&gt;
);
export default FormField;
</code></pre>
<h4 id="heading-imports">Imports:</h4>
<ul>
<li>The component imports the <code>FormFieldProps</code> type from the <code>@/types</code> module. This type contains the expected properties for a form field, such as <code>type</code>, <code>placeholder</code>, <code>name</code>, <code>register</code>, <code>error</code>, and <code>valueAsNumber</code>.</li>
</ul>
<h4 id="heading-input-element">Input Element:</h4>
<ul>
<li>The component renders an <code>&lt;input&gt;</code> element with attributes set based on the provided props (<code>type</code>, <code>placeholder</code>, <code>name</code>). </li>
<li>The <code>...register(name, { valueAsNumber })</code> syntax is used to register the input field with the form, enabling form state management.</li>
</ul>
<h4 id="heading-error-handling">Error Handling:</h4>
<ul>
<li>If there is a validation error, a <code>&lt;span&gt;</code> element is rendered, displaying the error message.</li>
</ul>
<h3 id="heading-create-the-form-component">Create the Form Component</h3>
<p>The <code>Form</code> component will utilize the <code>react-hook-form</code> library to manage the form state. It modularizes form fields by using our reusable <code>FormField</code> component.</p>
<p>Navigate to <code>app/components/Form.tsx</code> and update it with the code below:</p>
<pre><code class="lang-tsx">import { useForm } from "react-hook-form";
import { FormData } from "@/types";
import FormField from "./FormField";

function Form() {
  const {
    register,
    handleSubmit,
    formState: { errors },
    setError,
  } = useForm&lt;FormData&gt;();

  const onSubmit = async (data: FormData) =&gt; {
      console.log("SUCCESS", data);
  }

  return (
      &lt;form onSubmit={handleSubmit(onSubmit)}&gt;
        &lt;div className="grid col-auto"&gt;
          &lt;h1 className="text-3xl font-bold mb-4"&gt;
            Zod &amp; React-Hook-Form
          &lt;/h1&gt;
          &lt;FormField
            type="email"
            placeholder="Email"
            name="email"
            register={register}
            error={errors.email}
          /&gt;

          &lt;FormField
            type="text"
            placeholder="GitHub URL"
            name="githubUrl"
            register={register}
            error={errors.githubUrl}
          /&gt;

          &lt;FormField
            type="number"
            placeholder="Years of Experience (1 - 10)"
            name="yearsOfExperience"
            register={register}
            error={errors.yearsOfExperience}
            valueAsNumber
          /&gt;

          &lt;FormField
            type="password"
            placeholder="Password"
            name="password"
            register={register}
            error={errors.password}
          /&gt;

          &lt;FormField
            type="password"
            placeholder="Confirm Password"
            name="confirmPassword"
            register={register}
            error={errors.confirmPassword}
          /&gt;
          &lt;button type="submit" className="submit-button"&gt;
            Submit
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/form&gt;
  );
}

export default Form;
</code></pre>
<h4 id="heading-imports-1">Imports:</h4>
<ul>
<li>The <code>useForm</code> hook provides functionality for managing form state and validation.</li>
<li><code>FormData</code> represents the structure of the form data.</li>
<li><code>FormField</code> is our reusable form field component.</li>
</ul>
<h4 id="heading-form-component">Form Component:</h4>
<ul>
<li>Form-related functions and state variables are destructured from the <code>useForm</code> hook, which is explicitly typed with <code>FormData</code> to define the shape of the form data.</li>
<li>Within the form, the <code>FormField</code> components are rendered for different input fields such as email, GitHub URL, years of experience, password, and confirm password.</li>
</ul>
<h4 id="heading-run-code">Run Code:</h4>
<p>Import the <code>Form</code> component into <code>/app/page.tsx</code> file:</p>
<pre><code class="lang-tsx">"use client";
import Form from "./components/Form";

function Home() {

  return (
    &lt;main className="flex min-h-screen flex-col items-center justify-between p-24"&gt;
     &lt;Form /&gt;
    &lt;/main&gt;
  );
}

export default Home;
</code></pre>
<p>Visit <a target="_blank" href="http://localhost:3000/">http://localhost:3000/</a> to view the form:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-11-at-11.40.22.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="http://localhost:3000/">http://localhost:3000/</a></em></p>
<p>In summary, our <code>Form</code> component is a basic form structure that uses the <code>react-hook-form</code> library for state management and employs a reusable <code>FormField</code> component to handle the rendering and validation of individual form fields. </p>
<h2 id="heading-how-to-integrate-zod-for-schema-validation">How to Integrate Zod for Schema Validation</h2>
<p>Zod stands out as a schema declaration and validation library, with TypeScript as its primary focus. The term "schema" encompasses various data types, ranging from strings, numbers, and booleans to more complex objects.</p>
<h3 id="heading-define-a-form-schema-with-zod">Define a Form Schema with Zod</h3>
<p>Let's create a TypeScript-backed form schema using Zod for our form structure.</p>
<p>Head to your <code>/types.ts</code> file, add the new imports, and create a user schema with the code below:</p>
<pre><code class="lang-ts"> <span class="hljs-keyword">import</span> { z, ZodType } <span class="hljs-keyword">from</span> <span class="hljs-string">"zod"</span>; <span class="hljs-comment">// Add new import</span>

 <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> UserSchema: ZodType&lt;FormData&gt; = z
  .object({
    email: z.string().email(),
    githubUrl: z
      .string()
      .url()
      .includes(<span class="hljs-string">"github.com"</span>, { message: <span class="hljs-string">"Invalid GitHub URL"</span> }),
    yearsOfExperience: z
      .number({
        required_error: <span class="hljs-string">"required field"</span>,
        invalid_type_error: <span class="hljs-string">"Years of Experience is required"</span>,
      })
      .min(<span class="hljs-number">1</span>)
      .max(<span class="hljs-number">10</span>),
    password: z
      .string()
      .min(<span class="hljs-number">8</span>, { message: <span class="hljs-string">"Password is too short"</span> })
      .max(<span class="hljs-number">20</span>, { message: <span class="hljs-string">"Password is too long"</span> }),
    confirmPassword: z.string(),
  })
  .refine(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> data.password === data.confirmPassword, {
    message: <span class="hljs-string">"Passwords do not match"</span>,
    path: [<span class="hljs-string">"confirmPassword"</span>], <span class="hljs-comment">// path of error</span>
  });
</code></pre>
<h4 id="heading-imports-2">Imports:</h4>
<ul>
<li><code>z</code> is an instance of the Zod object. </li>
<li><code>ZodType</code> is a generic type that represents a Zod schema type for a specific data structure.</li>
</ul>
<h4 id="heading-user-schema">User Schema:</h4>
<ul>
<li><code>export const UserSchema: ZodType&lt;FormData&gt; = ...</code>: The <code>UserSchema</code> represents a Zod type that corresponds to the structure defined by the <code>FormData</code> type.</li>
<li><code>z.object({...})</code>: This part defines an object schema using Zod. The object has several fields, each with its own validation rules.</li>
<li>Inside the object, each field is defined with its own validation rules using Zod methods like <code>z.string()</code>, <code>z.url()</code>, <code>z.number()</code>, and <code>z.min()</code>. Optional custom error messages are provided for some of the fields.</li>
<li><code>z.refine((data) =&gt; data.password === data.confirmPassword, { /* ... */ });</code>: Adds a refinement to the schema to check if the <code>password</code> and <code>confirmPassword</code> fields match. If not, a custom error message is provided, and the error is associated with the <code>confirmPassword</code> field.</li>
</ul>
<h3 id="heading-how-to-integrate-zod-with-react-hook-form-for-validation">How to Integrate Zod with React-Hook-Form for validation</h3>
<p>Now that we've set up the Zod schema for the form, let's integrate it with our existing Form component. To do this, we'll use <code>zodResolver</code> from the <code>[@hookform](https://www.npmjs.com/package/@hookform/resolvers)</code> library.</p>
<p><code>zodResolver</code> is a resolver function that integrates the Zod schema validation with the form validation process. </p>
<p>Head over to the <code>app/components/Form.tsx</code> file and update it with the code below:</p>
<pre><code class="lang-tsx">// Update imports
import { FormData, UserSchema } from "@/types";
import { zodResolver } from "@hookform/resolvers/zod";

function Form() {
  const {
    register,
    handleSubmit,
    formState: { errors },
    setError,
  } = useForm&lt;FormData&gt;({
    resolver: zodResolver(UserSchema), // Apply the zodResolver
  });

{/* Existing Code...*/}

}
</code></pre>
<p>If you try submitting the form with empty input fields, you will see error messages on the browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-11-at-20.38.03.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error Messages - http://localhost:3000/</em></p>
<p>Additionally, our custom error messages, such as prompting users to provide a valid GitHub URL and checking if the passwords match, are demonstrated in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-11-at-20.59.18.png" alt="Image" width="600" height="400" loading="lazy">
<em>Custom Error Messages - http://localhost:3000/</em></p>
<h2 id="heading-how-to-handle-server-side-errors">How to Handle Server-Side Errors</h2>
<p>When creating forms, data integrity and type safety are very important, given that submitted data goes to the website's server. This leads us to the significance of handling server-side errors — an extra security measure to make sure data from the client is accurate and non-malicious.</p>
<h3 id="heading-how-to-implement-server-side-validation">How to Implement Server-Side Validation</h3>
<p>To implement server-side validation, we will leverage Next.js' backend capabilities to build a simple server. This server will receive and validate the data submitted through our form.</p>
<p>Navigate to <code>app/api/form/route.ts</code> and include the code below:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { UserSchema } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;
<span class="hljs-keyword">import</span> { NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">request: Request</span>) </span>{
  <span class="hljs-comment">// Retrieve the JSON data from the request body</span>
  <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> request.json();

  <span class="hljs-comment">// Use Zod to validate the received data against the UserSchema</span>
  <span class="hljs-keyword">const</span> result = UserSchema.safeParse(body);

  <span class="hljs-comment">// Check if the validation is successful</span>
  <span class="hljs-keyword">if</span> (result.success) {
    <span class="hljs-keyword">return</span> NextResponse.json({ success: <span class="hljs-literal">true</span> });
  }

  <span class="hljs-comment">// If validation errors, map them into an object</span>
  <span class="hljs-keyword">const</span> serverErrors = <span class="hljs-built_in">Object</span>.fromEntries(
    result.error?.issues?.map(<span class="hljs-function">(<span class="hljs-params">issue</span>) =&gt;</span> [issue.path[<span class="hljs-number">0</span>], issue.message]) || []
  );

  <span class="hljs-comment">// Respond with a JSON object containing the validation errors</span>
  <span class="hljs-keyword">return</span> NextResponse.json({ errors: serverErrors });
}
</code></pre>
<h4 id="heading-imports-3">Imports:</h4>
<ul>
<li>The <code>UserSchema</code> we defined earlier is imported.</li>
<li><code>NextResponse</code> from the <code>next/server</code> module, which allows us to craft server responses in a Next.js environment.</li>
</ul>
<h4 id="heading-post-function">POST Function:</h4>
<ul>
<li><code>const body = await request.json()</code>: Retrieves the JSON data from the request body and stores it in the <code>body</code> variable.</li>
<li><code>const result = UserSchema.safeParse(body)</code>: Utilizes the <code>safeParse</code> method provided by Zod to validate the received data against the <code>UserSchema</code>. The result contains information about whether the validation was successful and, if not, details about the validation issues.</li>
<li><code>if (result.success) { return NextResponse.json({ success: true }); }</code>: If the validation is successful, a JSON response with <code>{ success: true }</code> is sent.</li>
<li><code>const serverErrors = Object.fromEntries(/* ... */)</code>: If there are validation errors, the code maps them into an object with field names and corresponding error messages.</li>
<li><code>return NextResponse.json({ errors: serverErrors })</code>: Responds with a JSON object containing the validation errors.</li>
</ul>
<p>In your terminal, stop running the project and run <code>npm run dev</code> again to restart the server.</p>
<h3 id="heading-how-to-integrate-server-side-validation">How to Integrate Server-Side Validation</h3>
<p>To integrate the server-side validation, we have to update the <code>onSubmit</code> function in the Form component.</p>
<p>Head over to the <code>/app/components/Form.tsx</code> file and update the imports and <code>onSubmit</code> function:</p>
<pre><code class="lang-tsx">// Update import
import { FormData, UserSchema, ValidFieldNames } from "@/types";  
import axios from "axios";

function Form() {
{/* Existing Code... */}

  const onSubmit = async (data: FormData) =&gt; {
    try {
      const response = await axios.post("/api/form", data); // Make a POST request
      const { errors = {} } = response.data; // Destructure the 'errors' property from the response data

      // Define a mapping between server-side field names and their corresponding client-side names
      const fieldErrorMapping: Record&lt;string, ValidFieldNames&gt; = {
        email: "email",
        githubUrl: "githubUrl",
        yearsOfExperience: "yearsOfExperience",
        password: "password",
        confirmPassword: "confirmPassword",
      };

      // Find the first field with an error in the response data
      const fieldWithError = Object.keys(fieldErrorMapping).find(
        (field) =&gt; errors[field]
      );

      // If a field with an error is found, update the form error state using setError
      if (fieldWithError) {
        // Use the ValidFieldNames type to ensure the correct field names
        setError(fieldErrorMapping[fieldWithError], {
          type: "server",
          message: errors[fieldWithError],
        });
      }
    } catch (error) {
      alert("Submitting form failed!");
    }
  };
{/* Existing Code... */}
}
</code></pre>
<ul>
<li><code>axios</code> is used to make a POST request to the server endpoint <code>/api/form</code> with the form data.</li>
<li>The <code>errors</code> object is extracted from the response data.</li>
<li>A mapping (<code>fieldErrorMapping</code>) between field names and their corresponding <code>ValidFieldNames</code> is defined.</li>
<li>It then checks if there are any errors related to form fields by iterating over the <code>fieldErrorMapping</code> and finding the first field with an error.</li>
<li>If a field with an error is found, the <code>setError</code> function from <code>react-hook-form</code> is used to set an error for the corresponding field. The error type is marked as "server," and the error message comes from the server response.</li>
<li>If there's an error in the entire try block, it catches the error and displays an alert: "Submitting form failed!"</li>
</ul>
<p>Now, to test if we can receive errors from the server, we'll deliberately send improperly formatted data to the server. In your <code>onSubmit</code> function, replace the <code>data</code> object with the incorrect data in the code block below:</p>
<pre><code class="lang-tsx">
{/* Existing Code...*/}
  const onSubmit = async (data: FormData) =&gt; {

    try {
      // Update data sent in axios with incorrect data
      const response = await axios.post("/api/form", {
        email: "Not an email",
        githubUrl: "Not a URL",
        yearsOfExperience: "Hello",
        password: 1234,
        confirmPassword: 1234,
      }); // Make a POST request

{/* Existing Code...*/}
}
</code></pre>
<p>Fill the form in the browser normally and submit the form. </p>
<p>Inspect the "Network" tab within the browser's developer tools. You'll find error messages coming directly from the server, as demonstrated in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-12-at-10.21.47.png" alt="Image" width="600" height="400" loading="lazy">
<em>Server errors - http://localhost:3000/</em></p>
<p>If you're not getting any response from your server, remember to stop running your project in your terminal and run <code>npm run dev</code> again to re-start the server.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we built a form with React-Hook-Form and validated it with Zod. With Zod, we explored schema validation, customized error messages, and server-side errors. The integration of React-Hook-Form and Zod presents a powerful, developer-friendly solution to creating resilient forms.</p>
<p>You can reach out to me on <a target="_blank" href="https://twitter.com/dev_giftea">Twitter</a> if you have any questions.</p>
<p>You can check out the <a target="_blank" href="https://github.com/Giftea/zod-rhf-fcc">source code</a> and the deployed <a target="_blank" href="https://zod-rhf-fcc.vercel.app/">app</a>.</p>
<h3 id="heading-resources">Resources:</h3>
<ul>
<li><a target="_blank" href="https://zod.dev/">Zod Documentation</a></li>
<li><a target="_blank" href="https://zod.dev/ERROR_HANDLING?id=error-handling-in-zod">Zod Error Handling</a></li>
<li><a target="_blank" href="https://react-hook-form.com/get-started">React-Hook-Form Documentation</a></li>
<li><a target="_blank" href="https://www.npmjs.com/package/@hookform/resolvers">Hookform Resolvers</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
