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 current mode; 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=signup linkable.

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

React
// 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>  );}
React
// pages/Authentication.tsximport AuthForm from "../components/AuthForm";​export default function AuthenticationPage() {  return <AuthForm />;}
React
// App.tsx (children of root){ path: 'auth', element: <AuthenticationPage />, action: authAction }
Angular
// 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

TaskReactAngular
Read modeuseSearchParams().get('mode')mode = input<string>()
Switch mode<Link to="?mode=signup">[routerLink]="[]" [queryParams]="{ mode: 'signup' }"