Lesson 59 of 60 – Typing React Forms
98%

Typing React Forms

Forms are an important part of React applications. They are used for login pages, registration forms, student admission forms, search forms, contact forms, and many other applications. TypeScript allows us to correctly type form values, input events, select events, checkbox events, and form submission events.

Note: In React with TypeScript, controlled forms are commonly created using useState. Event types such as React.ChangeEvent and React.FormEvent help make form handling safer.

1. What is a React Form?

A form collects information from users through controls such as input fields, select boxes, checkboxes, and buttons.

<form>

    <input type="text" />

    <button type="submit">
        Submit
    </button>

</form>

2. Why Type React Forms?

TypeScript helps us define the expected type of form data.

  • Reduces incorrect values.
  • Improves editor suggestions.
  • Makes event handling safer.
  • Makes large forms easier to maintain.
  • Helps detect errors during development.

3. Controlled Components

A controlled component gets its value from React state.

const [name, setName] =
    useState<string>("");

return (
    <input
        value={name}
        onChange={(event) =>
            setName(event.target.value)
        }
    />
);

4. Typing Text Input

const handleNameChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    setName(event.target.value);
};

HTMLInputElement describes the input element that generated the change event.

5. Textarea in TypeScript

const [message, setMessage] =
    useState<string>("");

const handleMessageChange = (
    event: React.ChangeEvent<HTMLTextAreaElement>
): void => {

    setMessage(event.target.value);
};

Use HTMLTextAreaElement for a textarea change event.

6. Select Box

const [course, setCourse] =
    useState<string>("React");

const handleCourseChange = (
    event: React.ChangeEvent<HTMLSelectElement>
): void => {

    setCourse(event.target.value);
};

7. Checkbox State

const [agree, setAgree] =
    useState<boolean>(false);

const handleAgreeChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    setAgree(event.target.checked);
};

For a checkbox, use checked instead of value when reading its checked state.

8. Radio Buttons

const [gender, setGender] =
    useState<string>("");

const handleGenderChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    setGender(event.target.value);
};

Radio buttons can use a string state to store the selected value.

9. FormEvent

Use React.FormEvent to type a form submission event.

const handleSubmit = (
    event: React.FormEvent<HTMLFormElement>
): void => {

    event.preventDefault();

    console.log("Form submitted");
};

10. Preventing Default Form Submission

const handleSubmit = (
    event: React.FormEvent<HTMLFormElement>
): void => {

    event.preventDefault();

    console.log("Submitted");
};

preventDefault() prevents the browser from performing its default form submission behavior.

11. Form Interface

An interface can describe all the values in a form.

interface StudentForm {
    name: string;
    email: string;
    age: number;
    course: string;
}

This gives the form data a clear structure.

12. Form State with an Interface

const [form, setForm] =
    useState<StudentForm>({
        name: "",
        email: "",
        age: 0,
        course: ""
    });

The state must follow the structure defined by StudentForm.

13. Updating Form State

setForm({
    ...form,
    name: "Rahul"
});

The spread operator preserves the other form values while updating the name.

14. Updating Form State from Input

const handleNameChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    setForm({
        ...form,
        name: event.target.value
    });
};

15. Handling Number Inputs

HTML input values are received as strings, even when the input uses type="number".

const handleAgeChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    setForm({
        ...form,
        age: Number(event.target.value)
    });
};

16. Generic Input Handler

A form can use one handler for multiple text inputs.

const handleChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    const { name, value } =
        event.target;

    setForm({
        ...form,
        [name]: value
    });
};

The input's name attribute identifies which property should be updated.

17. Input Name Attribute

<input
    type="text"
    name="name"
    value={form.name}
    onChange={handleChange}
/>

<input
    type="email"
    name="email"
    value={form.email}
    onChange={handleChange}
/>

The name attribute should match the corresponding property in the form object.

18. Handling Select Values

const handleCourseChange = (
    event: React.ChangeEvent<HTMLSelectElement>
): void => {

    setForm({
        ...form,
        course: event.target.value
    });
};
<select
    value={form.course}
    onChange={handleCourseChange}
>

    <option value="">
        Select Course
    </option>

    <option value="React">
        React
    </option>

    <option value="TypeScript">
        TypeScript
    </option>

</select>

19. Handling Checkbox Values

const handleAgreeChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    setForm({
        ...form,
        agree: event.target.checked
    });
};

Use checked when storing a checkbox's boolean state.

20. Form Validation

TypeScript checks the structure and types of form data, while validation checks whether the values are acceptable.

if (form.name.trim() === "") {
    console.log("Name is required");
}

if (form.email.trim() === "") {
    console.log("Email is required");
}

21. Error State for Forms

interface FormErrors {
    name?: string;
    email?: string;
    course?: string;
}

const [errors, setErrors] =
    useState<FormErrors>({});

Optional properties allow only the fields with errors to be stored.

22. Submit Button

<button type="submit">
    Submit
</button>

When this button is clicked, the form's onSubmit handler is triggered.

23. Resetting Form State

const initialForm: StudentForm = {
    name: "",
    email: "",
    age: 0,
    course: ""
};

const [form, setForm] =
    useState<StudentForm>(initialForm);

const handleReset = (): void => {
    setForm(initialForm);
};

Resetting the state also resets all controlled form fields.

24. Form with Optional Fields

interface StudentForm {
    name: string;
    email: string;
    phone?: string;
    course: string;
}

The phone property is optional because it uses the ? symbol.

25. Login Form Example

interface LoginForm {
    email: string;
    password: string;
}

const [form, setForm] =
    useState<LoginForm>({
        email: "",
        password: ""
    });

const handleSubmit = (
    event: React.FormEvent<HTMLFormElement>
): void => {

    event.preventDefault();

    console.log(form);
};

26. Common React Form Typing Mistakes

  • Using the wrong event type.
  • Forgetting to type complex form state.
  • Storing number input values without conversion.
  • Using value instead of checked for checkbox state.
  • Forgetting event.preventDefault() on client-side form submission.
  • Using incorrect names for form fields.
  • Not handling optional or nullable values correctly.

27. React Native Forms

React Native forms use components such as TextInput, Button, and Pressable.

import {
    TextInput,
    Button,
    View
} from "react-native";

const [name, setName] =
    useState<string>("");

return (
    <View>

        <TextInput
            value={name}
            onChangeText={setName}
            placeholder="Enter name"
        />

        <Button
            title="Save"
            onPress={() => {
                console.log(name);
            }}
        />

    </View>
);

28. Typed Student Admission Form

interface AdmissionForm {
    name: string;
    mobile: string;
    course: string;
}

const [form, setForm] =
    useState<AdmissionForm>({
        name: "",
        mobile: "",
        course: ""
    });

const handleChange = (
    event: React.ChangeEvent<HTMLInputElement>
): void => {

    const { name, value } =
        event.target;

    setForm({
        ...form,
        [name]: value
    });
};

const handleSubmit = (
    event: React.FormEvent<HTMLFormElement>
): void => {

    event.preventDefault();

    console.log(
        "Admission Data:",
        form
    );
};

29. Best Practices for Typed Forms

  • Create an interface for complex form data.
  • Use controlled inputs with useState.
  • Use the correct event type for each form control.
  • Convert number input values using Number() when required.
  • Use checked for checkbox state.
  • Keep validation separate from TypeScript type checking.
  • Use optional properties for optional fields.
  • Reset the form by restoring the initial state.
  • Keep form handlers small and easy to understand.

30. Complete Typed React Form

import {
    useState
} from "react";

interface StudentForm {
    name: string;
    email: string;
    age: number;
    course: string;
    agree: boolean;
}

const initialForm: StudentForm = {
    name: "",
    email: "",
    age: 0,
    course: "",
    agree: false
};

function StudentFormPage() {

    const [form, setForm] =
        useState<StudentForm>(
            initialForm
        );

    const handleChange = (
        event: React.ChangeEvent<HTMLInputElement>
    ): void => {

        const {
            name,
            value
        } = event.target;

        setForm({
            ...form,
            [name]: name === "age"
                ? Number(value)
                : value
        });
    };

    const handleCourseChange = (
        event: React.ChangeEvent<HTMLSelectElement>
    ): void => {

        setForm({
            ...form,
            course: event.target.value
        });
    };

    const handleAgreeChange = (
        event: React.ChangeEvent<HTMLInputElement>
    ): void => {

        setForm({
            ...form,
            agree: event.target.checked
        });
    };

    const handleSubmit = (
        event: React.FormEvent<HTMLFormElement>
    ): void => {

        event.preventDefault();

        if (!form.name.trim()) {
            alert("Please enter your name");
            return;
        }

        if (!form.email.trim()) {
            alert("Please enter your email");
            return;
        }

        if (!form.agree) {
            alert(
                "Please accept the terms"
            );
            return;
        }

        console.log(
            "Student Data:",
            form
        );
    };

    const handleReset = (): void => {
        setForm(initialForm);
    };

    return (
        <form
            onSubmit={handleSubmit}
        >

            <h2>
                Student Admission Form
            </h2>

            <input
                type="text"
                name="name"
                value={form.name}
                placeholder="Enter name"
                onChange={handleChange}
            />

            <br />
            <br />

            <input
                type="email"
                name="email"
                value={form.email}
                placeholder="Enter email"
                onChange={handleChange}
            />

            <br />
            <br />

            <input
                type="number"
                name="age"
                value={form.age}
                onChange={handleChange}
            />

            <br />
            <br />

            <select
                value={form.course}
                onChange={
                    handleCourseChange
                }
            >

                <option value="">
                    Select Course
                </option>

                <option value="React">
                    React
                </option>

                <option value="TypeScript">
                    TypeScript
                </option>

                <option value="React Native">
                    React Native
                </option>

            </select>

            <br />
            <br />

            <label>

                <input
                    type="checkbox"
                    checked={form.agree}
                    onChange={
                        handleAgreeChange
                    }
                />

                I agree to the terms

            </label>

            <br />
            <br />

            <button type="submit">
                Submit
            </button>

            <button
                type="button"
                onClick={handleReset}
            >
                Reset
            </button>

        </form>
    );
}

export default StudentFormPage;

This complete example combines interfaces, typed form state, text inputs, email input, number conversion, select fields, checkbox state, form submission, validation, and form reset.

📌 Key Points

  • React forms commonly use controlled components.
  • Use interfaces to describe complex form data.
  • Use React.ChangeEvent for input changes.
  • Use React.FormEvent for form submission.
  • Use HTMLInputElement for normal input events.
  • Use HTMLSelectElement for select events.
  • Use HTMLTextAreaElement for textarea events.
  • Checkboxes normally use the checked property.
  • HTML number input values still arrive as strings and may need conversion.
  • React Native uses components such as TextInput and Button with their own event APIs.

🧠 Quick Quiz

Question: Which React event type is commonly used for form submission?