Welcome back! In the last lesson, you learned how to build a login form and protect certain routes in your React app. Now, let’s take the next step: allowing new users to create their own accounts. Registration is a key part of most web applications. It lets users sign up, create their own profiles, and access features that require authentication.
By the end of this lesson, you will know how to build a registration flow in your React app. This means you will be able to collect user information, send it to your backend, handle errors, and guide users to the next step after registration.
Before we dive into the registration flow, let’s quickly review how routing is set up in your app. This will help you see where the registration page fits in.
Here is a simplified version of your routing setup:
In this setup, the /register route points to the RegisterPage component. This is where your registration form will live. If you remember from the previous lesson, protected routes (like /shelf) are only accessible to logged-in users.
Let’s look at how the registration form is built. The form is responsible for collecting a username and password from the user.
Here is the main code for the registration form:
Explanation:
- The form uses React’s
useStateto keep track of theusername,password, and any error messages. - When the form is submitted, it calls
handleSubmit. - If registration is successful, the user is redirected to the login page.
- If there is an error (like a duplicate username), an error message is shown.
What the user sees:
- Two input fields: one for username, one for password.
- A Register button.
- If there’s an error, a message appears above the form.
Let's take a closer look
-
Three
useStatehooksusername/setUsername: Tracks the text input, making the field a controlled component (React holds the source of truth).password/setPassword: Same pattern for the password field.error/setError: Holds a user-visible message shown above the form. It’s cleared before each attempt to avoid stale errors.
-
e.preventDefault()- Stops the browser from doing a full page reload on submit.
- Keeps the flow inside the SPA so we can run async logic (call API, update UI state, navigate) without leaving the React app.
-
useNavigate()- Provides an imperative navigation function. After successful registration, we call
navigate('/login', { replace: true })to send the user to Login and replace history (so “Back” doesn’t return to the submitted form state).
- Provides an imperative navigation function. After successful registration, we call
-
apiClient.post('/auth/register', { username, password })- Encodes JSON, sets headers, and uses the previously resolved base URL from
apiUtils. - Returns the parsed body. We treat it as a backend envelope and check
success.
- Encodes JSON, sets headers, and uses the previously resolved base URL from
-
Success path
- If
success === true, we redirect to Login where the user will authenticate (keeping registration and login concerns cleanly separated).
- If
-
Error path
- If the server signals failure via an envelope or throws with
{ status, data }, we surface a clear message. - We handle duplicate username specifically (common statuses:
400,409), defaulting to a friendly message if the server doesn’t provide one. - All other failures display a generic message to avoid leaking internal errors to users.
- If the server signals failure via an envelope or throws with
This keeps the UI responsive, avoids reloads, and aligns the frontend strictly to the backend’s envelope shape and status codes.
When the user submits the form, the app needs to send the registration data to the backend API. This is done using the apiClient.post method.
Let’s look at the key part of the code again:
Explanation:
apiClient.post("/auth/register", { username, password })sends the user’s data to the backend.- If the backend returns a 409 status, it means the username is already taken. The app shows a helpful error message.
- For any other error, a generic error message is shown.
Example output:
If a user tries to register with a username that already exists, they will see:
If registration is successful, they are redirected to the login page.
After a user successfully registers, you want to guide them to the next step: logging in. This is handled by the navigate("/login") line in the handleSubmit function.
How it works:
- When the registration API call succeeds, the app uses the
useNavigatehook from React Router to send the user to the login page. - This makes the experience smooth and clear for the user.
What the user experiences:
- After submitting the form with valid data, the registration form disappears and the login page appears.
In this lesson, you learned how to build a registration flow in your React app. You saw how to:
- Add a registration route to your app’s router
- Build a registration form that collects user input
- Send registration data to the backend API
- Handle errors and show helpful messages
- Redirect users to the login page after successful registration
You are now ready to practice these steps yourself. In the next exercises, you will get hands-on experience building and testing the registration flow. This will help you understand how registration, login, and protected routes all work together to create a secure and user-friendly app. Good luck!
