Building the Registration Flow
Introduction: Why Registration Matters
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.
Quick Recap: App Structure and Routing
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.
Building the Registration Form
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.
