This post shows how to add an authentication page to a React app using React Router, correctly wiring routes and preparing the UI layer for user-based access and future scaling.
Click here for Act 3 · Scene 1
Table of Contents
- Overview
- Why Authentication Shows Up Now
- Architectural Foreshadowing (Thinking Ahead)
- Creating the AuthForm and AuthPage
- Making the Route Exist
- While I Was At It With Auth… A Button Component Was Born
- Mental Model
- Why this scene matters
Act 3 · Scene 2 : Fixing and Rendering the AuthPage
In this scene, I introduce authentication into a React MERN application by wiring an AuthPage into the routing system and laying the groundwork for user based access control.
Overview
With navigation and routing in place, the next logical step was to introduce authentication.
At first, I debated skipping it entirely. But the more I stared at the app, the more obvious it became that authentication would eventually be required not just to identify users, but to control what they are allowed to do.
So instead of pretending auth doesn’t exist, I decided to face it early.
This scene focuses on:
- Introducing an AuthPage
- Rendering it properly through the router
- Introducing a reusable Button component for shared UI behavior
The goal wasn’t to build auth fully yet, but to prepare the application for it.
Why Authentication Shows Up Now?
Authentication influences architecture decisions early on:
- What users can see
- What they can do
- What eventually needs protecting
If I delay it too long, it will sneak up on me later without sneakers and will force some ugly refactors. So it's best to face it head on now
Architectural Foreshadowing (Thinking Ahead)
Introducing authentication automatically implies a few future decisions.
First: data persistence.
Once users can sign up or log in, I will need a database to store user records securely. And fetch them when needed. I am not introducing the database yet, but it’s already part of the mental model for where this product is headed. We will talk more about it when we cross that bridge.
Second: backend control and scaling
Authentication almost always means a backend, unless I hand over everything to a managed service like Firebase. But for this project, I want more control over how authentication is handled, especially with scaling in mind.
Vertical scaling, in particular, will require being intentional about how auth logic is structured. More on that later. This is already looking mouth watering. I am salivating already. MAAAAAN! I am in love with this whole process.
So, for now, let’s get started with authentication, and what better place to start than the AuthForm? And just to quickly add, I will not be implementing Google authentication in here yet, at least for now.
Creating the AuthForm and AuthPage
I started by creating an AuthForm component inside a dedicated auth folder under components alongside its SCSS module.
Folder structure:
components/
auth/
AuthForm.tsx
AuthForm.module.scss
Then I created an AuthPage inside the pages directory to act as the route-level wrapper. The AuthPage inside the pages directory is to host the AuthForm.
Code below:
import AuthForm from "../components/auth/AuthForm";
const AuthPage = () => {
return <AuthForm />;
};
export default AuthPage;
This keeps routing concerns separate from UI logic. As Pages handle routing, while Components handle UI.
Making the Route Exist
Of course, nothing renders until the router knows about it.
And I hope you remember this image from the previous scene:
So back to App.tsx.
I added the /auth route to the route tree:
children: [
{ path: "/", element: <HomePage /> },
{ path: "/auth", element: <AuthPage /> },
];
With this change, the /auth route now renders correctly.
While I Was At It With Auth… A Button Component Was Born
While building the AuthForm, I noticed that I would be making use of a styled Button more than once.
So instead of hardcoding styles, I introduced a reusable Button component inside the shared ui folder:
interface ButtonProps {
children: React.ReactNode;
className?: string;
onClick?: () => void;
type: "submit" | "button";
}
const Button = (props: ButtonProps) => {
return (
<button
className={`${classes.button} ${props.className || ""}`}
onClick={props.onClick}
>
{props.children}
</button>
);
};
Later, I will move the ButtonProps out of this Button.tsx file. I will store it in another file, which I will call interfaces.
Mental Model
Right now:
- Routes unlock pages, well Auth page for now
- Pages host components
- UI building blocks reduce duplication
- Authentication exists as infrastructure, not a feature sprint
Why This Scene Matters
This scene shows that I:
- Think about users and permissions early
- Build reusable UI intentionally
- Prefer steady foundations over rushed features
Thanks for reading.
Let’s move on to the Next Act.
We in the Building…
Building in Progress…


Top comments (0)