Signup Form Example in React
A step-by-step guide to building an accessible React signup form with uncontrolled inputs, proper labeling, browser validation, and client-side submission
Building a form with great user experience and accessibility isn't all that easy - but it also isn't that hard once you know the things to pay attention to.
On this page we'll demonstrate the basics of building a good, accessible simple signup form with uncontrolled inputs. Since the inputs are uncontrolled, most of the points aren't specific to React.
1. Input field
First and foremost, start with an <input>
field. <input>
are the backbone of HTML forms and depending on the type
attribute value, browsers render these controls differently to assist the user in filling up the form. Common values include text
, email
, number
, url
, checkbox
, file
, etc.
<input type="email" />
2. Add label to input field
All <input>
tags should have an associated <label>
for accessibility purposes. This is necessary so that users of assistive technologies can tell what the input is for. Clicking or touching a label will also focus on the label's associated form control.
The <label>
and the <input>
are linked using for
(htmlFor
in React) and id
attributes respectively.
<label htmlFor="email-input">Email</label><input id="email-input" type="email" />
The alternative is to use aria-label
on <input>
but it is better to have a visible label using <label>
.
3. Wrap within a form
Next, the elements should be wrapped within a <form>
. This enables the browser's enter-to-submit behavior, aka users can hit Enter to submit the form.
<form><label htmlFor="email-input">Email</label><input id="email-input" type="email" /></form>
4. Form attributes
The <form>
tag accepts these attributes:
action
: Specifies the URL of the server that will receive the submitted form data.method
: Defines the HTTP method used to send the form data. Common values are:POST
Form data is sent as part of the request body.GET
: Form data is appended to the URL as search/query parameters.
enctype
: Specifies how the form data should be encoded when submitting it to the server. This is particularly important when you're uploading files.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><input id="email-input" type="email" /></form>
5. Submit button
While not all users are aware they can hit Enter to submit the form, submit buttons are universally understood mechanisms for form submissions. If the type
attribute of the <button>
is not specified, the default value used is <button type="submit">
.
When such <button>
s are used within a <form>
, triggering them will cause form submissions.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><input id="email-input" type="email" /><button>Sign up</button></form>
An alternative way to create a submit button is by using the <input>
tag with type="submit
.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><input id="email-input" type="email" /><input type="submit" value="Sign up"></form>
6. Input field name
attribute
<input>
elements with a name
attribute defined will be included in the form submission data as part of the name/value pairs.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><input id="email-input" name="userEmail" type="email" /><button>Sign up</button></form>
With the name
attribute specified, when the form is submitted:
- For
<form method="GET">
, a HTTPGET
request will be made to/users/signup?userEmail=john.doe@gmail.com
. - For
<form method="POST">
, a HTTPPOST
request will be made to/users/signup
and with the body asuserEmail=john.doe@gmail.com
.
If there are more form fields, the key-values pairs are concatenated with &
as the delimiter. E.g. userEmail=john.doe@gmail.com&password=securepassword123
.
The value of an <input>
element without a name
attribute is not included in the data submitted to the server. The name
attribute acts as a key for the form data, pairing it with the value
attribute of the input element. If the name
attribute is missing, the form data associated with that input will not be part of the HTTP request when the form is submitted.
Hence all form control input elements should have the name
attribute specified.
7. Other attributes for input field (e.g. autocomplete
)
By adding the autocomplete
attribute, browsers will offer suggestions based on the value and the user's autofill data such as addresses and payment methods.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><input autocomplete="email" id="email-input" name="userEmail" type="email" /><button>Sign up</button></form>
Possible values include email
, family-name
, new-password
, street-address
, etc. Refer to the full list of values on MDN. Specifying the autocomplete
attribute is especially useful for shipping and billing address forms on checkout pages.
8. Browser validation
Add a password field using <input type="password" />
.
By specifying attributes like required
, minlength
, pattern
, on <input>
, the browser can help to validate that the user's input matches these requirements before the form is submitted to the server.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><inputautocomplete="email"id="email-input"name="userEmail"requiredtype="email"/><label htmlFor="password-input">Password</label><inputautocomplete="new-password"id="password-input"minlength="8"name="userPassword"requiredtype="password"/><button>Sign up</button></form>
Note that browser validation alone is insufficient! Malicious personnel can directly hit your server endpoints without using your HTML form. You should still validate and sanitize all user input on the server.
9. Link form control descriptions using aria-describedby
For <input>
s which benefit from additional descriptions / hint text, they can be associated with the element that contain the descriptive text, which could be a <span>
, <div>
or any other suitable HTML elements.
<form method="POST" action="/users/signup"><label htmlFor="email-input">Email</label><inputautocomplete="email"id="email-input"name="userEmail"requiredtype="email"/><label htmlFor="password-input">Password</label><inputaria-describedby="password-hint"autocomplete="new-password"id="password-input"minlength="8"name="userPassword"requiredtype="password"/><div id="password-hint">Your password must be at least 8 characters long.</div><button>Sign up</button></form>
When a user focuses on the password input field, assistive technologies like screen readers will read the input label along with the additional instructions provided in the <div>
. This helps users understand the requirements for the password field more clearly.
10. Submission via fetch()
The current form will make a HTTP POST
request to /users/signup
upon submission, which causes a full page navigation and that might not always be desirable.
We can modify the form to make a client-side HTTP POST
request via fetch()
. The main changes to make are:
- Call
event.preventDefault()
to prevent the browser from navigating away - Use
new FormData(formElement)
to get the input fields data from the form element - Send the data to the server API endpoint via
fetch()
function SignupForm() {async function handleSubmit(event) {// Prevent default behavior, which is a navigationevent.preventDefault();const formElement = event.target;const formData = new FormData(formElement);const response = await fetch('/users/signup', {method: 'POST',body: JSON.stringify({email: formData.get('userEmail'),password: formData.get('userPassword'),}),headers: { 'Content-Type': 'application/json' },});const data = await response.json();if (!response.ok) {// Handle error responsereturn;}// Handle success flow}return (<form onSubmit={handleSubmit}><label htmlFor="email-input">Email</label><inputautocomplete="email"id="email-input"name="userEmail"requiredtype="email"/><label htmlFor="password-input">Password</label><inputaria-describedby="password-hint"autocomplete="new-password"id="password-input"minlength="8"name="userPassword"requiredtype="password"/><div id="password-hint">Your password must be at least 8 characters long.</div><button>Sign up</button></form>);}
There you have it, a fully-accessible sign up form in React.
What you need to know for interviews
How to independently build an accessible form in React with validation and submission.