Split Login page using tailwind css & shadcn
import { useState } from "react"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
import { Link, useNavigate } from "react-router-dom"
import { toast } from "sonner"
import { Loader2, Eye, EyeOff, CheckCircle2, Zap } from "lucide-react"
import { Button } from "@hms/shared/components/ui/button"
import { Input } from "@hms/shared/components/ui/input"
import { Label } from "@hms/shared/components/ui/label"
const loginSchema = z.object({
email: z.string().email("Please enter a valid email address"),
password: z.string().min(6, "Password must be at least 6 characters"),
})
type LoginFormValues = z.infer<typeof loginSchema>
export default function SplitLogin() {
const navigate = useNavigate()
const [showPassword, setShowPassword] = useState(false)
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
})
async function onSubmit(data: LoginFormValues) {
await new Promise((r) => setTimeout(r, 1000))
localStorage.setItem("access_token", "mock-token")
localStorage.setItem(
"user",
JSON.stringify({ email: data.email, name: data.email.split("@")[0], role: "PATIENT" })
)
toast.success("Welcome back!")
navigate("/dashboard")
}
return (
<div className="flex min-h-svh">
{/* Left Panel - Dark Branding */}
<div className="hidden lg:flex lg:w-1/2 bg-zinc-950 text-white flex-col justify-between p-10">
<div>
<h1 className="text-3xl font-bold tracking-tight">Welcome Back</h1>
<p className="mt-2 text-zinc-400">Sign in to continue to your workspace</p>
</div>
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-zinc-800">
<CheckCircle2 className="h-5 w-5 text-zinc-300" />
</div>
<div>
<h3 className="font-semibold">Secure Access</h3>
<p className="text-sm text-zinc-400">
Your data is protected with enterprise-grade security
</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-zinc-800">
<Zap className="h-5 w-5 text-zinc-300" />
</div>
<div>
<h3 className="font-semibold">Fast Performance</h3>
<p className="text-sm text-zinc-400">
Lightning-fast access to all your resources
</p>
</div>
</div>
</div>
<p className="text-sm text-zinc-500">
© {new Date().getFullYear()} Desk. All rights reserved.
</p>
</div>
{/* Right Panel - Login Form */}
<div className="flex w-full lg:w-1/2 items-center justify-center bg-white p-6 sm:p-10">
<div className="w-full max-w-md space-y-8">
<div>
<h2 className="text-2xl font-bold text-zinc-900">Sign In</h2>
<p className="mt-1 text-sm text-zinc-500">
Enter your credentials to access your account
</p>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="email" className="text-zinc-700">
Email
</Label>
<Input
id="email"
type="email"
placeholder="name@example.com"
autoComplete="email"
className="h-11 rounded-lg border-zinc-300 bg-white text-zinc-900 placeholder:text-zinc-400 focus-visible:border-zinc-900 focus-visible:ring-zinc-900/20"
aria-invalid={!!errors.email}
{...register("email")}
/>
{errors.email && (
<p className="text-sm text-destructive">{errors.email.message}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="password" className="text-zinc-700">
Password
</Label>
<div className="relative">
<Input
id="password"
type={showPassword ? "text" : "password"}
placeholder="Enter your password"
autoComplete="current-password"
className="h-11 rounded-lg border-zinc-300 bg-white pr-10 text-zinc-900 placeholder:text-zinc-400 focus-visible:border-zinc-900 focus-visible:ring-zinc-900/20"
aria-invalid={!!errors.password}
{...register("password")}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600"
tabIndex={-1}
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? (
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</button>
</div>
{errors.password && (
<p className="text-sm text-destructive">{errors.password.message}</p>
)}
</div>
<Button
type="submit"
size="lg"
className="w-full h-11 rounded-lg bg-zinc-900 text-white hover:bg-zinc-800"
disabled={isSubmitting}
>
{isSubmitting && <Loader2 className="animate-spin" />}
{isSubmitting ? "Signing in..." : "Sign In"}
</Button>
</form>
<p className="text-sm text-zinc-500 text-center">
Don't have an account?{" "}
<Link to="/auth/register" className="text-zinc-900 font-medium hover:underline">
Register
</Link>
</p>
</div>
</div>
</div>
)
}
Example output

About the author
Technology Enthusiast and voracious reader with a demonstrated history of working in the computer software industry. Skilled in PHP, JavaScript, NodeJS, Angular, MySQL, MongoDB, Web3, Product Development, Project Management, and Teamwork.
Comments
Join the discussionNo comments yet. Be the first to comment.