Adding Authentication to React AppsEntry 6 of 11
Updating the UI Based on Auth State
A loader on the root route returns the token, and any component reads it with useRouteLoaderData('root') to show or hide UI.
Notes
- Give the root route
id: 'root'andloader: tokenLoader. - Navigation and pages use
useRouteLoaderData('root')to show Login vs Logout, or hide Edit/Delete buttons for logged-out users. - React Router revalidates loaders after every action, so the root loader re-runs after login and logout and the UI updates automatically.
- Hiding buttons is UX only. Protect the routes (Entry 8) and enforce on the backend.
What / Why / When
- What
App-wide access to auth state.
- Why
UI reflects whether the user is logged in, without Context or Redux.
- When
Conditional navigation links, action buttons, user-specific UI.
Code compare
// App.tsx{ path: '/', element: <RootLayout />, errorElement: <ErrorPage />, id: 'root', loader: tokenLoader, children: [/* ... */],}// components/MainNavigation.tsximport { Form, NavLink, useRouteLoaderData } from "react-router";import type { AuthToken } from "../util/auth";export default function MainNavigation() { const token = useRouteLoaderData("root") as AuthToken; const isLoggedIn = token && token !== "EXPIRED"; return ( <header> <nav> <ul> <li> <NavLink to="/" end> Home </NavLink> </li> <li> <NavLink to="/events">Events</NavLink> </li> {!isLoggedIn && ( <li> <NavLink to="/auth?mode=login">Authentication</NavLink> </li> )} {isLoggedIn && ( <li> <Form action="/logout" method="post"> <button>Logout</button> </Form> </li> )} </ul> </nav> </header> );}// components/EventItem.tsxconst token = useRouteLoaderData("root") as AuthToken;{ token && token !== "EXPIRED" && ( <menu> <Link to="edit">Edit</Link> <button onClick={startDeleteHandler}>Delete</button> </menu> );}@Component({ selector: "app-main-navigation", imports: [RouterLink, RouterLinkActive], template: ` <header> <nav> <ul> <li> <a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }" >Home</a > </li> <li><a routerLink="/events" routerLinkActive="active">Events</a></li> @if (!isAuthenticated()) { <li><a routerLink="/auth" [queryParams]="{ mode: 'login' }">Authentication</a></li> } @else { <li><button (click)="logout()">Logout</button></li> } </ul> </nav> </header> `,})export class MainNavigationComponent { private authService = inject(AuthService); protected isAuthenticated = this.authService.isAuthenticated; logout() { this.authService.logout(); }}Differences and gotchas
- React's auth UI updates through loader revalidation; changing
localStorageoutside an action or navigation won't update it. - Angular's auth UI updates through the signal, immediately and anywhere.
Quick reference
| Task | React | Angular |
|---|---|---|
| Expose auth state | Root loader: tokenLoader, id: 'root' | AuthService.isAuthenticated signal |
| Read it | useRouteLoaderData('root') | inject(AuthService).isAuthenticated() |
| UI refresh trigger | Loader revalidation after actions | Signal change |