diff --git a/docs/docs/guide/react.mdx b/docs/docs/guide/react.mdx
index 2984e13..117c269 100644
--- a/docs/docs/guide/react.mdx
+++ b/docs/docs/guide/react.mdx
@@ -457,9 +457,796 @@ Use controlled inputs when all three conditions above are not met.
There are many third party form libraries that can help you manage forms in a more declarative way. For example, [React Hook Form](https://react-hook-form.com/) and [Formik](https://formik.org/).
-
- Discuss and write down when to use third party form libraries.
-
+#### When to use third party form libraries
+
+1. Real time / instant validation of input values.
+
+There are cases where an input value is required to be validated immediately without having to wait for the user to submit the form. For example:
+
+```tsx title="email-form.tsx" lineNumbers
+"use client";
+
+import { useForm } from "react-hook-form";
+import { useState } from "react";
+
+// Server function to check if email exists
+async function checkEmailExists(email: string): Promise {
+ const response = await fetch("/api/check-email", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ email }),
+ });
+ const data = await response.json();
+ return data.exists;
+}
+
+export default function EmailForm() {
+ const {
+ register,
+ handleSubmit,
+ formState: { errors, isValidating },
+ setError,
+ clearErrors,
+ } = useForm();
+ const [isCheckingEmail, setIsCheckingEmail] = useState(false);
+
+ const validateEmail = async (email: string) => {
+ if (!email) return "Email is required";
+
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) {
+ return "Invalid email format";
+ }
+
+ setIsCheckingEmail(true);
+ try {
+ const exists = await checkEmailExists(email);
+ if (exists) {
+ return "This email is already registered";
+ }
+ return true;
+ } catch (error) {
+ return "Failed to validate email";
+ } finally {
+ setIsCheckingEmail(false);
+ }
+ };
+
+ const onSubmit = async (data: any) => {
+ // Send the payload to the server
+ await fetch("/api/check-email", {
+ method: "POST",
+ body: JSON.stringify(data),
+ });
+ };
+
+ return (
+
+ );
+}
+```
+
+The example above validates if the email is not duplicated with the existing emails in the database. This is a good use case for third party form libraries, as it can show the validation error UI feedbacks such as red borders, error message, etc. immediately without having to wait for the user to submit the form.
+
+2. Conditional rendering of input fields.
+
+There are cases where you want to conditionally render an input field based on the value of another input field. For example, take a look at this case of activity creation form:
+
+There is a field called `type`. The value of `type` is either "event", "regular", and "project"
+
+- If the value of `type` is "event", then there should be an array of fields for the event dates.
+- If the value of `type` is "regular", then there should be an array of fields for the regular schedules per day, along with the start and end time of the whole activity period.
+- If the value of `type` is "project", then there should be a field for the start and end date of the project.
+
+```tsx title="activity-form.tsx" lineNumbers
+"use client";
+
+import { useForm, useFieldArray } from "react-hook-form";
+
+type ActivityType = "event" | "regular" | "project";
+
+interface ActivityFormData {
+ name: string;
+ type: ActivityType;
+ // Event fields
+ eventDates?: { date: string }[];
+ // Regular fields
+ regularSchedules?: {
+ day: string;
+ startTime: string;
+ endTime: string;
+ }[];
+ activityStartDate?: string;
+ activityEndDate?: string;
+ // Project fields
+ projectStartDate?: string;
+ projectEndDate?: string;
+}
+
+export default function ActivityForm() {
+ const { register, control, watch, handleSubmit } = useForm({
+ defaultValues: {
+ name: "",
+ type: "event",
+ eventDates: [{ date: "" }],
+ regularSchedules: [{ day: "Monday", startTime: "", endTime: "" }],
+ },
+ });
+
+ const activityType = watch("type");
+
+ const {
+ fields: eventDateFields,
+ append: appendEventDate,
+ remove: removeEventDate,
+ } = useFieldArray({
+ control,
+ name: "eventDates",
+ });
+
+ const {
+ fields: scheduleFields,
+ append: appendSchedule,
+ remove: removeSchedule,
+ } = useFieldArray({
+ control,
+ name: "regularSchedules",
+ });
+
+ const onSubmit = (data: ActivityFormData) => {
+ console.log("Form submitted:", data);
+ // Remove the unnecessary fields from the payload
+ switch (activityType) {
+ case "event":
+ return {
+ name: data.name,
+ eventDates: data.eventDates,
+ };
+ case "regular":
+ return {
+ name: data.name,
+ regularSchedules: data.regularSchedules,
+ activityStartDate: data.activityStartDate,
+ activityEndDate: data.activityEndDate,
+ };
+ case "project":
+ return {
+ name: data.name,
+ projectStartDate: data.projectStartDate,
+ projectEndDate: data.projectEndDate,
+ };
+ }
+
+ // Send the payload to the server
+ await fetch("/api/activities", {
+ method: "POST",
+ body: JSON.stringify(payload),
+ });
+ };
+
+ return (
+
+ );
+}
+```
+
+This is a good use case for third party form libraries, as it can handle the conditional rendering of input fields and preprocess the payload data before sending it to the server.
+
+3. Dynamic form arrays
+
+There are cases where you want to dynamically add or remove input fields in a form. For example, take a look at this case of inputting divisions in an activity creation form:
+
+a. There is a field called `divisions`.
+b. The value of `divisions` is an array of objects.
+c. Each object has a `name`, `description`, and `number_of_volunteers_needed` fields.
+d. The user can add or remove divisions by clicking a button.
+
+```tsx title="divisions-form.tsx" lineNumbers
+"use client";
+
+import { useForm, useFieldArray } from "react-hook-form";
+
+interface Division {
+ name: string;
+ description: string;
+ number_of_volunteers_needed: number;
+}
+
+interface DivisionsFormData {
+ activityName: string;
+ divisions: Division[];
+}
+
+export default function DivisionsForm() {
+ const {
+ register,
+ control,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({
+ defaultValues: {
+ activityName: "",
+ divisions: [
+ {
+ name: "",
+ description: "",
+ number_of_volunteers_needed: 0,
+ },
+ ],
+ },
+ });
+
+ const { fields, append, remove } = useFieldArray({
+ control,
+ name: "divisions",
+ });
+
+ const onSubmit = async (data: DivisionsFormData) => {
+ // Preprocess the payload before sending to server
+ const payload = {
+ activityName: data.activityName,
+ divisions: data.divisions.filter(
+ (division) =>
+ division.name.trim() !== "" &&
+ division.number_of_volunteers_needed > 0
+ ),
+ };
+
+ console.log("Processed payload:", payload);
+
+ // Send to server
+ await fetch("/api/activities", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(payload),
+ });
+ };
+
+ return (
+
+ );
+}
+```
+
+This is a good use case for third party form libraries, as it can handle the dynamic form arrays and preprocess the payload data before sending it to the server.
+
+#### Hybrid approach between third party form libraries and server functions
+
+You can use a hybrid approach between third party form libraries and server functions. For example, you can use React Hook Form to handle the client side validation, and call the server function to handle the server side validation and data processing.
+
+```tsx title="registration-form.tsx" lineNumbers
+// Client Component
+"use client";
+
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { useEffect } from "react";
+import { registerUser } from "./actions"; // This is the server function
+import FormSchema from "./form-schema";
+
+interface RegistrationFormData {
+ name: string;
+ email: string;
+ password: string;
+ confirmPassword: string;
+ recaptcha: string;
+}
+
+export default function RegistrationForm() {
+ const {
+ register,
+ handleSubmit,
+ watch,
+ setValue,
+ formState: { errors, isSubmitting },
+ } = useForm();
+
+ const password = watch("password");
+
+ useEffect(() => {
+ // Load reCAPTCHA script
+ const script = document.createElement("script");
+ script.src = "https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY";
+ script.async = true;
+ document.body.appendChild(script);
+
+ return () => {
+ document.body.removeChild(script);
+ };
+ }, []);
+
+ // React Hook Form will handle the submit event
+ const onSubmit = async (data: RegistrationFormData) => {
+ try {
+ // Execute reCAPTCHA
+ const token = await window.grecaptcha.execute("YOUR_SITE_KEY", {
+ action: "register",
+ });
+
+ // Set the reCAPTCHA token
+ const dataWithRecaptcha = {
+ ...data,
+ recaptcha: token,
+ };
+
+ // Validate the input values in the client side first
+ const validatedData = FormSchema.safeParse(dataWithRecaptcha);
+ if (!validatedData.success) {
+ toast.error(validatedData.error.message);
+ return;
+ }
+
+ // If valid, call the server function
+ await registerUser(validatedData.data);
+ toast.success("Registration successful!");
+ } catch (error) {
+ toast.error("Registration failed. Please try again.");
+ }
+ };
+
+ return (
+
+ );
+}
+```
## Compound pattern