255 lines
9.3 KiB
Plaintext
255 lines
9.3 KiB
Plaintext
---
|
|
title: Signup Form Example in React
|
|
description: 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.
|
|
|
|
```jsx
|
|
<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.
|
|
|
|
```jsx
|
|
<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 <kbd>Enter</kbd> to submit the form.
|
|
|
|
```jsx
|
|
<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.
|
|
|
|
```jsx
|
|
<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 <kbd>Enter</kbd> 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.
|
|
|
|
```jsx
|
|
<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`.
|
|
|
|
```jsx
|
|
<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.
|
|
|
|
```jsx
|
|
<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 HTTP `GET` request will be made to `/users/signup?userEmail=john.doe@gmail.com`.
|
|
- For `<form method="POST">`, a HTTP `POST` request will be made to `/users/signup` and with the body as `userEmail=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.
|
|
|
|
```jsx
|
|
<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](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values). 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.
|
|
|
|
```jsx
|
|
<form method="POST" action="/users/signup">
|
|
<label htmlFor="email-input">Email</label>
|
|
<input
|
|
autocomplete="email"
|
|
id="email-input"
|
|
name="userEmail"
|
|
required
|
|
type="email"
|
|
/>
|
|
<label htmlFor="password-input">Password</label>
|
|
<input
|
|
autocomplete="new-password"
|
|
id="password-input"
|
|
minlength="8"
|
|
name="userPassword"
|
|
required
|
|
type="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.
|
|
|
|
```jsx
|
|
<form method="POST" action="/users/signup">
|
|
<label htmlFor="email-input">Email</label>
|
|
<input
|
|
autocomplete="email"
|
|
id="email-input"
|
|
name="userEmail"
|
|
required
|
|
type="email"
|
|
/>
|
|
<label htmlFor="password-input">Password</label>
|
|
<input
|
|
aria-describedby="password-hint"
|
|
autocomplete="new-password"
|
|
id="password-input"
|
|
minlength="8"
|
|
name="userPassword"
|
|
required
|
|
type="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()`
|
|
|
|
```jsx
|
|
function SignupForm() {
|
|
async function handleSubmit(event) {
|
|
// Prevent default behavior, which is a navigation
|
|
event.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 response
|
|
return;
|
|
}
|
|
|
|
// Handle success flow
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<label htmlFor="email-input">Email</label>
|
|
<input
|
|
autocomplete="email"
|
|
id="email-input"
|
|
name="userEmail"
|
|
required
|
|
type="email"
|
|
/>
|
|
<label htmlFor="password-input">Password</label>
|
|
<input
|
|
aria-describedby="password-hint"
|
|
autocomplete="new-password"
|
|
id="password-input"
|
|
minlength="8"
|
|
name="userPassword"
|
|
required
|
|
type="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.
|