Adding Authentication to React AppsEntry 2 of 11
Switching Login and Signup with Query Params
One auth page handles both login and signup, switching modes with a query parameter (/auth?mode=login or /auth?mode=signup).
Notes
- Login and signup forms usually have the same fields, so one form component serves both.
useSearchParams()reads the currentmode; the form's labels change based on it.- A
<Link to="?mode=signup">changes only the query string, keeping the user on/auth. - Storing the mode in the URL (instead of
useState) lets the route action read it too (Entry 3), and makes/auth?mode=signuplinkable.
What / Why / When
- What
URL-driven form mode.
- Why
One component, shareable links, and the action knows which endpoint to call.
- When
Forms with multiple modes sharing the same fields.
Code compare
// components/AuthForm.tsximport { Form, Link, useSearchParams } from "react-router";import classes from "./AuthForm.module.css";export default function AuthForm() { const [searchParams] = useSearchParams(); const isLogin = searchParams.get("mode") === "login"; return ( <Form method="post" className={classes.form}> <h1>{isLogin ? "Log in" : "Create a new user"}</h1> <p> <label htmlFor="email">Email</label> <input id="email" type="email" name="email" required /> </p> <p> <label htmlFor="password">Password</label> <input id="password" type="password" name="password" required /> </p> <div className={classes.actions}> <Link to={`?mode=${isLogin ? "signup" : "login"}`}> {isLogin ? "Create new user" : "Login"} </Link> <button>Save</button> </div> </Form> );}// pages/Authentication.tsximport AuthForm from "../components/AuthForm";export default function AuthenticationPage() { return <AuthForm />;}// App.tsx (children of root){ path: 'auth', element: <AuthenticationPage />, action: authAction }// auth.component.ts@Component({ selector: "app-auth", imports: [RouterLink, ReactiveFormsModule], template: ` <form [formGroup]="form" (ngSubmit)="onSubmit()"> <h1>{{ isLogin() ? "Log in" : "Create a new user" }}</h1> <label for="email">Email</label> <input id="email" type="email" formControlName="email" /> <label for="password">Password</label> <input id="password" type="password" formControlName="password" /> <div> <a [routerLink]="[]" [queryParams]="{ mode: isLogin() ? 'signup' : 'login' }"> {{ isLogin() ? "Create new user" : "Login" }} </a> <button type="submit">Save</button> </div> </form> `,})export class AuthComponent { mode = input<string>(); // bound from ?mode= (withComponentInputBinding) protected isLogin = computed(() => this.mode() === "login"); protected form = new FormGroup({ email: new FormControl("", { nonNullable: true, validators: [Validators.required, Validators.email], }), password: new FormControl("", { nonNullable: true, validators: [Validators.required, Validators.minLength(6)], }), }); onSubmit() { // Entry 3 }}Quick reference
| Task | React | Angular |
|---|---|---|
| Read mode | useSearchParams().get('mode') | mode = input<string>() |
| Switch mode | <Link to="?mode=signup"> | [routerLink]="[]" [queryParams]="{ mode: 'signup' }" |