Start by setting up a new Vite project with React and JavaScript. Open your terminal and run the following commands to create the project and install the dependency for form handling.
npm create vite@latest contact-form -- --template react
cd contact-form
npm install
npm install react-hook-form
Create the contact form component using React Hook Form for validation and management. The form includes name, email, and message fields with the same validation rules as the Angular version (name required, email required and valid format, message required with 10-20 characters). Instead of a notification service, an alert will be used to show the submission message.
import React from 'react';
import { useForm } from 'react-hook-form';
import './ContactForm.css';
const ContactForm = () => {
const { register, handleSubmit, formState: { errors, isValid } } = useForm({
mode: 'onChange',
});
const onSubmit = (data) => {
console.log('Form valid:', isValid);
console.log('Message errors:', errors.message);
alert('Thank you for your message! We will get back to you soon.');
};
return (
<div className="form-container">
<form onSubmit={handleSubmit(onSubmit)} className="form-group">
<div className="form-group">
<label htmlFor="name">Name:</label>
<input
id="name"
{...register('name', { required: 'Name is required.' })}
/>
{errors.name && <span className="validation-error">{errors.name.message}</span>}
</div>
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
id="email"
{...register('email', {
required: 'Email is required.',
pattern: {
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
message: 'Invalid format',
},
})}
/>
{errors.email && <span className="validation-error">{errors.email.message}</span>}
</div>
<div className="form-group">
<label htmlFor="message">Message:</label>
<textarea
id="message"
{...register('message', {
required: 'Message is required.',
minLength: { value: 10, message: 'At least 10 characters' },
maxLength: { value: 20, message: 'At most 20 characters' },
})}
/>
{errors.message && <span className="validation-error">{errors.message.message}</span>}
</div>
<button type="submit" disabled={!isValid}>Submit</button>
</form>
</div>
);
};
export default ContactForm;
Add CSS to style the contact form, matching the Angular version’s appearance with a centered layout, consistent input styling, and matching button and error message styles.
.form-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 15px;
width: 300px;
}
label {
margin-bottom: 5px;
font-size: 16px;
font-weight: bold;
text-align: left;
}
input, textarea {
padding: 10px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 5px;
width: 100%;
box-sizing: border-box;
}
textarea {
resize: vertical;
}
button {
padding: 10px 20px;
font-size: 16px;
color: white;
background-color: #220c67;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #55557c;
}
button:disabled {
background-color: gray;
cursor: not-allowed;
opacity: 0.6;
}
.validation-error {
color: red;
font-size: 14px;
margin-top: 5px;
}
Update the main application component to render the contact form without the notification provider or display component, as the alert replaces the notification system.
import React from 'react';
import ContactForm from './ContactForm';
const App = () => {
return <ContactForm />;
};
export default App;
Ensure the index.html file is set up to load the React application. Vite generates this file by default, but confirm it includes the necessary script.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Contact Form</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
Update the main.jsx file to render the App component, as Vite uses .jsx for JavaScript projects.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
To run the application, execute npm run dev in the terminal. This starts the Vite development server, and you can view the contact form at http://localhost:5173. The form validates the name (required), email (required and valid format), and message (required, 10-20 characters). The submit button is disabled until the form is valid, and submitting triggers a browser alert with the message “Thank you for your message! We will get back to you soon.”, matching the Angular version’s functionality. The plain CSS ensures the form has a similar appearance to the Angular version, with a centered layout, consistent input styling, and matching colors for the button and error messages.
Now, let’s explain key aspects of the ContactForm.js code.
The form uses React Hook Form’s useForm hook to manage form state and validation. The mode: ‘onChange’ option ensures validation occurs as the user types, providing immediate feedback.
const { register, handleSubmit, formState: { errors, isValid } } = useForm({
mode: 'onChange',
});
The register function is used to bind form inputs to the form state, with validation rules specified for each field. For example, the email field requires a valid email format using a regular expression.
<input
id="email"
{...register('email', {
required: 'Email is required.',
pattern: {
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
message: 'Invalid format',
},
})}
/>
The handleSubmit function wraps the onSubmit handler, ensuring the form only submits if all validations pass. The onSubmit function logs the form’s validity and any message field errors, then triggers the alert.
const onSubmit = (data) => {
console.log('Form valid:', isValid);
console.log('Message errors:', errors.message);
alert('Thank you for your message! We will get back to you soon.');
};
Error messages are conditionally displayed below each input using the errors object from formState. For example, the message field’s error message is shown if the input fails validation.
{errors.message && <span className="validation-error">{errors.message.message}</span>}
The submit button is disabled when the form is invalid, using the isValid property from formState, matching the Angular version’s behavior.
<button type="submit" disabled={!isValid}>Submit</button>
These aspects ensure the form is functional, user-friendly, and visually consistent with the Angular version, while using plain JavaScript and a simple alert for feedback.