New

InviteTeam

Team invite form with email input, role selector dropdown, pending invites list with status badges.

Auth & Onboardinginviteteamcollaborationonboardingemail

Dependencies

shadcn/ui components needed:

npx shadcn@latest add lucide-react

How to use this component

Copy the code below into your project. Make sure you have the required shadcn/ui dependencies installed. Then import and use the component in your pages or layouts.

Code

1"use client";
2
3import { useState } from "react";
4import { UserPlus, X, Clock, Check, Mail } from "lucide-react";
5
6interface Invite { email: string; role: string; status: "pending" | "accepted" }
7
8export default function InviteTeam() {
9 const [email, setEmail] = useState("");
10 const [role, setRole] = useState("member");
11 const [invites, setInvites] = useState<Invite[]>([
12 { email: "sarah@example.com", role: "admin", status: "accepted" },
13 { email: "james@example.com", role: "member", status: "pending" },
14 { email: "lina@example.com", role: "viewer", status: "pending" },
15 ]);
16
17 const addInvite = () => {
18 if (!email) return;
19 setInvites([...invites, { email, role, status: "pending" }]);
20 setEmail("");
21 setRole("member");
22 };
23
24 const removeInvite = (emailToRemove: string) => {
25 setInvites(invites.filter((inv) => inv.email !== emailToRemove));
26 };
27
28 return (
29 <div className="mx-auto w-full max-w-lg rounded-2xl border border-zinc-200 bg-white p-8 shadow-xl dark:border-zinc-800 dark:bg-zinc-950">
30 <div className="mb-6 flex items-start gap-3">
31 <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-indigo-600">
32 <UserPlus className="h-5 w-5 text-white" />
33 </div>
34 <div>
35 <h2 className="text-lg font-bold text-zinc-900 dark:text-zinc-50">Invite your team</h2>
36 <p className="text-sm text-zinc-500 dark:text-zinc-400">Add members by email to collaborate together</p>
37 </div>
38 </div>
39
40 <form onSubmit={(e) => { e.preventDefault(); addInvite(); }} className="mb-6 flex gap-2">
41 <div className="relative flex-1">
42 <Mail className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-zinc-400" />
43 <input type="email" placeholder="colleague@example.com" value={email} onChange={(e) => setEmail(e.target.value)} className="w-full rounded-lg border border-zinc-300 bg-transparent py-2.5 pl-9 pr-3.5 text-sm outline-none transition-colors placeholder:text-zinc-400 focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 dark:border-zinc-700 dark:text-zinc-100" />
44 </div>
45 <select value={role} onChange={(e) => setRole(e.target.value)} className="rounded-lg border border-zinc-300 bg-transparent px-3 py-2.5 text-sm outline-none transition-colors focus:border-violet-500 dark:border-zinc-700 dark:text-zinc-100">
46 <option value="admin">Admin</option>
47 <option value="member">Member</option>
48 <option value="viewer">Viewer</option>
49 </select>
50 <button type="submit" className="rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white transition-all hover:bg-violet-500 active:scale-[0.98]">Send</button>
51 </form>
52
53 <div>
54 <h3 className="mb-3 text-xs font-semibold uppercase tracking-wider text-zinc-400">Invitations ({invites.length})</h3>
55 <div className="space-y-2">
56 {invites.map((inv) => (
57 <div key={inv.email} className="flex items-center gap-3 rounded-lg border border-zinc-100 px-4 py-3 dark:border-zinc-800">
58 <div className="flex h-8 w-8 items-center justify-center rounded-full bg-zinc-100 text-xs font-semibold uppercase text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">{inv.email[0]}</div>
59 <div className="flex-1 min-w-0">
60 <p className="truncate text-sm font-medium text-zinc-900 dark:text-zinc-100">{inv.email}</p>
61 <p className="text-xs capitalize text-zinc-500 dark:text-zinc-400">{inv.role}</p>
62 </div>
63 <span className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${
64 inv.status === "accepted" ? "bg-emerald-50 text-emerald-700 dark:bg-emerald-900/20 dark:text-emerald-400" : "bg-amber-50 text-amber-700 dark:bg-amber-900/20 dark:text-amber-400"
65 }`}>
66 {inv.status === "accepted" ? <Check className="h-3 w-3" /> : <Clock className="h-3 w-3" />}
67 {inv.status === "accepted" ? "Joined" : "Pending"}
68 </span>
69 <button onClick={() => removeInvite(inv.email)} className="rounded-md p-1 text-zinc-400 transition-colors hover:bg-zinc-100 hover:text-zinc-600 dark:hover:bg-zinc-800 dark:hover:text-zinc-300">
70 <X className="h-3.5 w-3.5" />
71 </button>
72 </div>
73 ))}
74 </div>
75 </div>
76 </div>
77 );
78}

Related Auth & Onboarding Components

Command Palette

Search for a command to run...