LoginPage
A ready-to-use, full-page login screen. It combines a decorative background (YusrBusBackground) with a two-column card that holds the login form on one side and an illustration on the other.
LoginForm lives in a separate file purely for readability — together, they form a single feature.
Preview
How to Use
The page is a default Next.js route component. Drop it inside your app/(auth)/login/ directory (or wherever your auth routes live) and it works out of the box.
app/
└── (auth)/
└── login/
├── page.tsx ← LoginPage (default export)
└── loginForm.tsx ← LoginForm (named export)
page.tsx
"use client"
import { YusrBusBackground } from "@yusr_systems/ui";
import { LoginForm } from "./loginForm";
export default function LoginPage() {
return (
<div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10">
<YusrBusBackground />
<div className="w-full max-w-sm md:max-w-4xl">
<LoginForm />
</div>
</div>
);
}
loginForm.tsx
"use client"
import type { Setting } from "@/app/core/data/setting";
import { login, updateLoggedInUser, useAppDispatch } from "@/app/core/state/store";
import {
ApiConstants,
LoginRequest,
SystemPermissions,
User,
type ValidationRule,
Validators,
YusrApiHelper,
} from "@yusr_systems/core";
import {
Button, Card, CardContent, Checkbox, cn,
Field, FieldDescription, FieldGroup,
PasswordField, TextField, useEntityForm,
} from "@yusr_systems/ui";
import { Loader2 } from "lucide-react";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
export function LoginForm({ className, ...props }: React.ComponentProps<"div">) {
// ... see full source
}
What happens on submit
- Validates the form — stops if any field is missing.
- Saves (or clears) the email and username in
localStoragebased on the Remember Me checkbox. - Calls
POST /LoginviaYusrApiHelper. - On success, dispatches
loginandupdateLoggedInUserto the Redux store, then navigates to the first permitted route (or acallbackUrlquery param if one exists).
LoginPage Props
LoginPage accepts no props. It is a self-contained route component.
LoginForm Props
LoginForm forwards all div props to its root element.
| Prop | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
className | string | No | undefined | "mt-8" | Extra Tailwind classes merged onto the root <div>. |
...props | React.ComponentProps<"div"> | No | — | data-testid="login-form" | Any valid HTML div attribute is forwarded to the root element. |
note
All business logic (API call, routing, store dispatch) is internal. There are no callback props — the component manages everything itself.
Dependencies
| Name | Role | Link |
|---|---|---|
YusrBusBackground | Decorative animated background | yusr_systems/ui |
useEntityForm | Form state and validation hook | yusr_systems/ui |
YusrApiHelper | HTTP utility for calling the Yusr API | yusr_systems/core |
LoginRequest | Request model/DTO for the login endpoint | yusr_systems/core |
SystemPermissions.getFirstPermissionPath | Resolves the first route a user is allowed to visit | yusr_systems/core |
login / updateLoggedInUser | Redux actions | app/core/state/store |
useAppDispatch | Typed Redux dispatch hook | app/core/state/store |
Validators | Built-in validation rules (e.g., required) | yusr_systems/core |
lucide-react | Icon library (Loader2 spinner) | https://lucide.dev |
next/navigation | useRouter, useSearchParams | https://nextjs.org/docs/app/api-reference/functions/use-router |
Notes
- Remember Me persists
companyEmailandusernametolocalStorageunder the keysremembered_emailandremembered_username. It intentionally never saves the password. - After a successful login, navigation is wrapped in a
setTimeout(..., 10)to give the Redux store one tick to settle before the route change. - The form validates all three fields (
companyEmail,username,password) as required. Errors are shown inline and cleared as the user types. - The right-hand illustration column is hidden on mobile (
hidden md:block) — on small screens the form takes the full card width. - The
callbackUrlquery parameter is respected: if present, the user is redirected there after login instead of their first permission path.