Users Admin Page React block for Tailwind CSS v4
v1.0.1Updated in 1.9.0
A React users admin page: team members and invitations with seats, roles with a permissions matrix and a save bar, and the audit log.
The header shows the seats in use ("8 of 10 seats", turning amber from 80%), Invite people and a menu with Export members CSV (downloads a ; separated file that opens cleanly in Excel) and Security settings. Switch between Members, Roles and permissions and Audit log with a click or the arrow keys; the panel slides the way you went. In Members, a warning lists who has no two-step verification with Send reminder; search by name or email, sort by name, role or last activity, change a role in its picker (it saves with a spinner, a failure rolls back with a toast, and the last administrator cannot be demoted), remove someone from the row menu after a confirmation (you and the owner cannot be removed), and in Invitations resend or revoke an invitation. Invite people opens the invite dialog (a bottom sheet on phones): paste or type addresses, pick a role, add a message and send; addresses already in the team are refused. With every seat taken it first offers Add 5 seats. In Roles and permissions, pick a role card (or a column heading) to highlight it, create a role with New role (name, description, copy permissions from a role), and tick permissions in the matrix: requirements follow along, changed cells get a dot, and a save bar slides up with the count, Discard and Save (mod+s while it shows). Leaving the tab with unsaved changes asks first. On phones the matrix picks one role at a time with chips and the save bar floats above the edge. In Audit log, search, filter by user, action, record type, severity or IP, change the period, expand a row for its changes, and export the rows as CSV; your own changes on this page appear at the top. Toasts confirm each action. Sections rise in once on mount; reduced motion keeps them still.
Users Admin Page is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add users-admin-page, together with Alert, Alert Dialog, Audit Log, Avatar, Badge and 25 more and the npm packages motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add users-admin-pageUsers Admin Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import UsersAdminPage from @/components/blocks/users-admin-page.
// app/(admin)/team/page.tsx (the shell lives in app/(admin)/layout.tsx: <AdminShell>{children}</AdminShell>)
"use client"
import { UsersAdminPage } from "@/components/blocks/users-admin-page"
export default function TeamPage() {
return (
<UsersAdminPage
companyName="Northwind Labs"
onInvite={async ({ emails, role }) => {
await fetch("/api/team/invitations", { method: "POST", body: JSON.stringify({ emails, role }) })
}}
onRoleChange={async (member, role) => {
await fetch(`/api/team/${member.id}`, { method: "PATCH", body: JSON.stringify({ role }) })
}}
onPermissionsSave={async (permissions) => {
await fetch("/api/roles", { method: "PUT", body: JSON.stringify(permissions) })
}}
/>
)
}Props
companyNamedefault"Northwind Labs"stringThe workspace named in the description ("Manage who has access to Northwind Labs").
membersMemberData[]The team; defaults to the eight people of Northwind Labs (USERS_ADMIN_SAMPLE.members). The member with isYou cannot change their own role or leave.
invitesPendingInvite[]Pending invitations; defaults to USERS_ADMIN_SAMPLE.invites (one pending, one expired). Invitations that have not expired hold a seat.
rolesMemberRole[]The roles a member can have: Administrator, Manager, Sales, Accounting, Read only (USERS_ADMIN_SAMPLE.roles).
permissionRolesPermissionRole[]The matrix's columns, same ids as roles; the locked Administrator holds every permission (USERS_ADMIN_SAMPLE.permissionRoles).
permissionGroupsPermissionGroup[]Six groups (Clients, Deals, Invoices, Reports, Automations, Settings and team) with 4 to 6 permissions and their requirements (USERS_ADMIN_SAMPLE.permissionGroups).
permissionsPermissionsValueThe saved permissions, controlled (role id to granted permission ids); pair it with onPermissionsSave.
defaultPermissionsPermissionsValueThe saved permissions while uncontrolled; defaults to USERS_ADMIN_SAMPLE.permissions.
onPermissionsSave(value: PermissionsValue) => (void | Promise<unknown>)The save bar's Save (and mod+s). A promise keeps it saving; a rejection keeps the bar with its message. Default: a simulated request.
defaultPermissionsDraftPermissionsValueEdits already pending when the page opens (the save bar showing); USERS_ADMIN_SAMPLE.permissionsDraft has three.
onDirtyChange(dirty: boolean) => voidThe matrix differs from the saved value, or no longer does; hide a phone tab bar while true so one bar holds the bottom edge.
auditEventsAuditEvent[]The audit log, newest first; defaults to 120 events over the last 30 days (USERS_ADMIN_SAMPLE.auditEvents). Changes made on this page are added on top.
auditActionsRecord<string, AuditActionDef>Verb, tone, group and icon per action key; defaults to USERS_ADMIN_SAMPLE.auditActions with icons per key.
auditTargetTypesRecord<string, string>Record type names for the filter and the export (member, role, client, invoice, deal, settings, login).
defaultAuditFilterFilterGroupThe audit log's first filter; usersAdminAuditTypeFilter(["invoice"]) shows only invoices.
auditPeriodDaysdefault7numberThe audit log's period on open: the last n days up to now; 0 shows all time.
seatsdefault"{ used: 8, total: 10 }"{ used: number; total: number }Seats in the plan. Pending invitations reserve seats; with none free, inviting offers to add seats first.
seatStepdefault5numberHow many seats "Add seats" adds to the plan.
tab"members" | "roles" | "audit"The open tab, controlled; pair it with onTabChange.
defaultTabdefault"members""members" | "roles" | "audit"The tab open on mount while uncontrolled.
onTabChange(tab: UsersAdminTab) => voidCalled on a tab switch (after the unsaved changes question, when there is one).
require2fadefaulttruebooleanThe workspace requires two-step verification: members without it get a warning with Send reminder.
canManagedefaulttruebooleanFalse is the read-only view: no invite, role changes, removals, new roles or matrix edits, and an info alert says why.
saveShortcutdefault"mod+s"stringSaves the matrix while it has unsaved changes; "" turns the key off.
inviteOpenbooleanThe invite dialog, controlled (the shell's "Invite a teammate" command can open it).
defaultInviteOpendefaultfalsebooleanOpens the invite dialog on mount (the seats dialog when every seat is taken).
onInviteOpenChange(open: boolean) => voidCalled when the invite dialog opens or closes.
securityHrefdefault"/settings/security"stringWhere "Security settings" in the header menu goes.
onInvite(input: { emails: string[]; role: string; message?: string }) => (void | Promise<unknown>)Sends invitations; the page adds them to Invitations. Default: a simulated request.
onRoleChange(member: MemberData, role: string) => (void | Promise<unknown>)A member's new role; a rejection rolls the picker back and shows its message. The last administrator cannot be demoted.
onRemove(member: MemberData) => (void | Promise<unknown>)Removes a member after the confirmation; the seat frees up.
onResendInvite(invite: PendingInvite) => (void | Promise<unknown>)Sends an invitation again and restarts its seven days.
onRevokeInvite(invite: PendingInvite) => (void | Promise<unknown>)Revokes an invitation; a rejection brings the row back.
onRoleCreate(role: { name: string; description?: string; copyFrom?: string }) => (void | Promise<unknown>)Creates a role from the New role dialog; the page adds its column with the copied permissions. A rejection shows under the name.
onExport(members: MemberData[]) => (void | Promise<unknown>)Export members CSV. Default: downloads members-<date>.csv with ; separators and a byte order mark.
onRemindTwoFactor(members: MemberData[]) => (void | Promise<unknown>)Send reminder, with the members who have no two-step verification.
onAddSeats(count: number) => (void | Promise<unknown>)Adds seatStep seats from the seats dialog; then the invite form opens.
labelsdefault"DEFAULT_USERS_ADMIN_PAGE_LABELS"Partial<UsersAdminPageLabels>Every visible string, English by default (USERS_ADMIN_PAGE_LABELS_EN); USERS_ADMIN_PAGE_LABELS_RO is the Romanian set. Nested keys carry the composed items' strings (membersTable, permissionsMatrix, auditLog, auditFilters, auditPeriod, usageMeter).
localedefault"en-US"stringIntl locale for numbers, dates and relative times.
nowDate | numberThe reference time for last activity, invitations and the audit period; default the live clock.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Accent of the invite button, the tabs, the selected role, the checkboxes and the save bar.
radiusdefault"md""sm" | "md" | "lg"Corner size of the role cards and the skeleton panels (12 / 16 / 24px).
loadingdefaultfalsebooleanSkeletons in the real layout: the header, the seats, the member rows, the role cards and the matrix.
animateIndefaulttruebooleanThe header and the panel rise in once when the page mounts on the client; off in thumbnails and under reduced motion.
linkComponentdefault"<a>"(props: LinkComponentProps) => ReactNodeThe router link for the header's links; default the surrounding LinkProvider's (the admin shell's).
classNamestringClasses for the root.
classNamesPartial<Record<UsersAdminPageSlot, string>>Classes for header, seats, panels, members, twoFactor, readOnly, roles, roleList, roleCard, permissions, saveBar, audit, seatsDialog and roleDialog.
Parts
DEFAULT_USERS_ADMIN_PAGE_LABELS
constantEvery English string of the members, roles and audit log tabs, the seats bar and the save bar. Spread it and override what you translate.
USERS_ADMIN_PAGE_LABELS_EN
constantThe English set that DEFAULT_USERS_ADMIN_PAGE_LABELS points to, kept so older imports keep working.
USERS_ADMIN_PAGE_LABELS_RO
constantA ready Romanian set of every string. Pass it as labels for a Romanian back office.
generateUsersAdminSample
functionBuilds the demo team in English or Romanian: members, invitations, seats, roles with their permissions and the audit log.
USERS_ADMIN_SAMPLE
constantThe English demo team built by generateUsersAdminSample. Replace it with data from your API.
usersAdminActor
functionTurns a member into the actor of an audit log entry (id, name, avatar).
usersAdminAuditTypeFilter
functionBuilds a filter group of the audit log by record type (for example only invoices), with stable ids for server rendering.
USERS_ADMIN_NOW
constantThe fixed "now" of the sample (September 24, 2026, 4:40 PM) that last seen and log times are measured from.
Examples
Roles with 3 unsaved changes
defaultPermissionsDraft: changed cells carry a dot and the save bar offers Discard and Save (mod+s).
Audit log filtered to invoices
defaultTab="audit", a record type filter on Invoice and the last 30 days.
Invite with every seat taken
8 members and 2 pending invitations fill 10 seats: inviting offers Add 5 seats first.
Read only (Ryan Mitchell)
canManage={false}: no invite, fixed roles, a read-only matrix and an info alert.
Loading
loading: the header, the seats, the rows and the matrix as skeletons.
Brand tone, large radius
tone="brand", radius="lg" on the roles tab.
Localized: Romanian
USERS_ADMIN_PAGE_LABELS_RO, locale="ro-RO" and generateUsersAdminSample("ro"): the same page for a Romanian workspace.
Dependencies
Files
- components/blocks/users-admin-page.tsx
- components/blocks/users-admin-page-data.ts
FAQ
Is the React Users Admin Page block free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Users Admin Page?
Run npx wingo-ui@latest add users-admin-page. It also adds the Wingo UI items it builds on (Alert, Alert Dialog, Audit Log, Avatar, Badge and 25 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/users-admin-page.
How does Users Admin Page behave on phones?
Invite people opens the invite dialog (a bottom sheet on phones): paste or type addresses, pick a role, add a message and send; addresses already in the team are refused. On phones the matrix picks one role at a time with chips and the save bar floats above the edge.
See also
More React blocks
Articles using this component
- Role Management UI in React: Members, Roles, Audit Log
Build it: screens and apps9 min read
- Nextjs Admin Dashboard Tutorial: 5 Screens, Phone-Ready
Build it: screens and apps23 min read