Role Management UI in React: Mitglieder, Rollen, Audit-Log
Jedes B2B-Produkt erreicht irgendwann den Tag, an dem ein Kunde fragt: „Kann meine Buchhalterin Rechnungen sehen, aber keine Kunden löschen?“ Für diese Frage brauchst du eine Role Management UI: einen Team-Screen, auf dem ein Admin Leute einlädt, jedem eine Rolle gibt, festlegt, was jede Rolle darf, und später nachsehen kann, wer was geändert hat. Wer danach sucht, findet Block-Galerien mit einer Mitgliedertabelle und einem Einladungsdialog und Dribbble-Shots von einem Raster aus Checkboxen. Kaum jemand erklärt, wie die Teile zusammenspielen: Seats, Abhängigkeiten zwischen Berechtigungen, ungespeicherte Änderungen, der Audit-Trail und das Layout auf dem Smartphone. Dieses Tutorial baut den Screen in Next.js aus Teilen von Wingo UI, mit dem Code, den du dafür brauchst.
Was gehört in eine User Role Management UI?
Drei Tabs decken ab, was Admins wirklich tun, und zwar in der Reihenfolge, in der sie es tun:
- Mitglieder und Einladungen. Das Team mit einem Rollen-Picker pro Zeile, Status, letzter Aktivität und dem Stand der Zwei-Faktor-Anmeldung, dazu die offenen Einladungen. Oben sitzt eine Seat-Anzeige, denn beim Einladen stößt man an die Grenzen des Plans.
- Rollen und Berechtigungen. Eine Karte pro Rolle und eine Matrix aus Rollen und Berechtigungen, gruppiert nach Modul (Kunden, Deals, Rechnungen, Berichte, Einstellungen und Team). Eine Save Bar sammelt die Änderungen.
- Audit-Log. Wer hat wann was an welchem Datensatz getan, von welcher IP aus, mit Filtern und einem CSV-Export für den Sicherheitsfragebogen, den dein Kunde früher oder später schickt.
Ob Admins dem Screen vertrauen, entscheiden die Details. Der letzte Administrator lässt sich nicht herabstufen, niemand entfernt den Owner oder sich selbst, und eine gescheiterte Anfrage stellt den alten Wert wieder her und sagt das auch. Eine User Role Management UI, die Erfolg meldet und die Änderung dann still verliert, ist schlimmer als gar keine.
Block aus einer Galerie oder kompletter Admin-Screen?
Starte mit einem Block, wenn die Rollen feststehen, und mit einem kompletten Screen, sobald Kunden Rollen selbst bearbeiten können. Die meisten Treffer für ein React User Management Template sind Blöcke: eine Mitgliedertabelle und ein Einladungsdialog. Die Angaben stammen von der jeweiligen öffentlichen Seite, Stand November 2026:
Hat dein Produkt drei feste Rollen und wird nie ein Kunde eigene verlangen, reicht die kostenlose Tabelle von shadcnblocks: Eine Rollenauswahl pro Zeile ist dann das ganze Feature. Sobald Rollen bearbeitbar werden, brauchst du die Matrix, das Prinzip aus Entwurf und Speichern und die Historie. Genau diesen Screen baut dieser Artikel. Die Teile von Wingo UI sind Pro; die Preise beginnen bei $8 im Monat.
Wie baust du die Team-Seite in Next.js auf?
Leg den Team-Screen in eine einzige Route: eine Server-page.tsx, die die Daten lädt, eine Client-View, die die Handler verdrahtet, und einen Katalog der Berechtigungen, den beide Seiten importieren. Die Seite liegt in der Admin-Route-Group aus dem Tutorial zum Next.js Admin Dashboard, das die Admin Shell und das Modul server/dal.ts mit requireAdmin() einrichtet. Installier den Block (er bringt die Tabelle, die Matrix und das Log mit):
Halt den Berechtigungskatalog in einem einfachen Modul ohne "use client". Die Matrix rendert ihn, und der Server validiert gegen dieselbe Liste. So können die beiden nicht auseinanderlaufen:
Lass die id des Administrators bei admin: Der Schutz für den letzten Administrator im Block sucht genau nach dieser Rolle.
Die View ist eine schlanke Client-Komponente, die die Ergebnisse der Server Actions in die Promises übersetzt, die der Block erwartet. Wird ein Promise abgelehnt, rollt der Block die Änderung zurück und zeigt die Meldung an:
page.tsx lädt die Zeilen, berechnet canManage aus der Session und rendert <TeamView />. Mit canManage={false} wird die Seite schreibgeschützt und erklärt, warum. Das ist besser, als das Team vor Leuten zu verstecken, die es nur sehen müssen.
Wie geht der Mitglieder-Tab mit Einladungen und Seats um?
Zähl offene Einladungen als belegte Seats, denn beim Einladen geht dem Plan der Platz aus. Im Block belegt eine Einladung einen Seat, bis sie abläuft: Die Anzeige unter dem Titel zeigt die belegten Seats mit einer Zeile wie „2 seats held by invitations“ und wechselt ab 80% in ihre Warnfarbe. Wenn Mitglieder und Einladungen alle Seats belegen, bietet „Invite people“ zuerst an, Seats hinzuzufügen (seatStep, standardmäßig fünf, über onAddSeats), und öffnet erst danach das Formular.
Eine gute Invite Team Members UI nimmt an, was Leute einfügen: Die Members Table zerlegt eine Liste wie Emma Carter <emma@northwind.app>; daniel@northwind.app in einzelne Adressen und listet die abgelehnten unter „Not added“ mit Grund auf (ungültig, schon Mitglied, schon eingeladen). Sind es mehr Adressen als freie Seats, bleibt „Send“ deaktiviert, und die Tabelle sagt, warum.
Rollenwechsel in einer Zeile sind optimistisch: Der Picker zeigt die neue Rolle mit einem Spinner, und bei einer Ablehnung springt sie samt Fehlermeldung zurück. Der Block verweigert das Herabstufen des letzten Administrators, bevor er dich überhaupt aufruft. Deine Action changeRole muss es trotzdem ebenfalls verweigern, denn eine Server Action lässt sich auch ohne den Button aufrufen.
$ npx wingo-ui@latest add users-admin-pageWie gestaltest du eine Permissions Matrix UI, die man lesen kann?
Eine Permissions Matrix UI scheitert auf zwei Arten: zu viele Zeilen zum Überfliegen und Checkboxen, die Zustände erlauben, die dein Backend ablehnt. Die Permissions Matrix löst beides mit ein paar Regeln, die sich auch dann lohnen, wenn du deine eigene baust.
- Nach Modul gruppieren, darin die Verben. Die Zeilen sind gruppiert (Kunden, Rechnungen), mit einer Gruppen-Checkbox, die einen Strich zeigt, wenn nur ein Teil aktiv ist. Nutz in allen Modulen dieselben Verben (ansehen, bearbeiten, exportieren), damit sich eine Zeile in jeder Gruppe gleich liest.
- Abhängigkeiten festlegen.
requires: ["invoices.view"]auf „Rechnungen ausstellen“ heißt: Wer das Recht bekommt, bekommt auch „Rechnungen ansehen“, und wer „Rechnungen ansehen“ verliert, verliert auch „Rechnungen ausstellen“. Die Zellen, die sich dadurch von selbst geändert haben, blinken einmal auf, und Screenreader sagen die Änderung an. - Gefährliche Rechte markieren.
dangerous: truesetzt einen Warnpunkt an Exporte und Löschungen, also an genau die Berechtigungen, nach denen ein Security-Review fragt. - Die oberste Rolle sperren. Eine Spalte mit
lockedhat alle Rechte und erklärt das bei Hover, Fokus oder langem Druck. So wundert sich niemand, warum sich der Administrator nicht bearbeiten lässt. - Den Diff zeigen. Gib den gespeicherten Wert als
compareTomit, dann bekommen abweichende Zellen einen Punkt und eine Tönung, und die Toolbar zeigt ihre Anzahl.
Für Rollen, die mehr als Ja oder Nein brauchen, macht mode="levels" aus jeder Zelle eine Auswahl aus None / View / Edit / Full.
$ npx wingo-ui@latest add permissions-matrixWarum eine Save Bar, statt jede Checkbox einzeln zu speichern?
Weil eine geänderte Berechtigung jedes Mitglied mit dieser Rolle betrifft. Landen die Änderungen in einem Entwurf, kann ein Admin eine Rolle komplett einrichten, die Anzahl sehen („3 unsaved changes“) und einmal speichern oder alles verwerfen. Der Block zählt mit countPermissionChanges(draft, saved, { ignoreRoles }) und nutzt eine Save Bar aus dem Settings Layout, die mit Verwerfen und Speichern hochgleitet, dazu mod+s, solange sie sichtbar ist. Wer mit ungespeicherten Änderungen den Tab wechselt, wird vorher gefragt. Die Rolle eines einzelnen Mitglieds speichert dagegen pro Klick: Das ist eine klare Entscheidung über eine Person.
Was wird aus der Matrix auf dem Smartphone?
Sie zeigt jeweils eine Rolle. Bei 390px passt ein Raster aus fünf Rollen und zwanzig Berechtigungen nicht hinein, und seitliches Scrollen versteckt die Rollennamen, während du Häkchen setzt. Deshalb scrollt die Matrix im Standardlayout auf dem Smartphone nicht seitwärts. Unter 768px bietet sie über mobileLayout zwei Layouts: "accordion" (eine Karte pro Rolle mit einem Switch pro Berechtigung und pro Modul) oder "role-picker" (Rollen-Chips über einer einzigen Liste). Die Users Admin Page nutzt den Rollen-Picker, weil Admins meist eine Rolle nach der anderen bearbeiten.
Stapel nie zwei Leisten am selben Rand. Die Save Bar schwebt über dem unteren Rand und respektiert die Safe Area, und onDirtyChange sagt deiner Shell, wann sie ihre Tab Bar auf dem Smartphone ausblenden soll, solange Änderungen offen sind. Der Mitglieder-Tab folgt derselben Regel wie jede responsive Tabelle: Zeilen werden zu Karten mit einem Rollen-Picker in voller Breite, und Einladungsdialog und Menüs öffnen sich als Bottom Sheets.
Was sollte das Audit-Log festhalten?
Jede Änderung, die ein Admin auf diesem Screen macht, und die Sicherheitsereignisse drumherum: verschickte und angenommene Einladungen, Rollenwechsel, Entfernungen, Änderungen an den Berechtigungen einer Rolle, fehlgeschlagene Anmeldungen und Exporte. Jedes Event im Audit Log trägt actor, action, target, severity, ip, device und eine Liste changes mit Vorher- und Nachher-Werten, die die Zeile beim Aufklappen als Diff zeigt.
Schreib diese Zeilen in derselben Transaktion wie die Änderung. Der Block setzt die Änderungen dieser Seite als sofortiges Feedback oben ins Log, aber die Quelle der Wahrheit ist deine Tabelle. Der Audit-Tab des Blocks filtert und exportiert die auditEvents, die du übergibst, im Browser. Lade also ein begrenztes Zeitfenster, etwa die letzten 90 Tage. Für ein Log, das dafür zu groß ist, renderst du das Audit Log selbst mit mode="server", onQueryChange, hasMore und onLoadMore und gibst ihm ein onExport, das die CSV auf dem Server baut statt aus den Zeilen auf dem Bildschirm.
$ npx wingo-ui@latest add audit-logWo setzt du die Berechtigungen durch?
Auf dem Server, und zwar jedes Mal. Die Matrix verhindert unmögliche Zustände im Browser, und savePermissions prüft sie gegen denselben Katalog noch einmal:
Die Helfer resolvePermissionDependencies und countPermissionChanges werden ebenfalls exportiert, allerdings aus einem Client-Modul. Der Server liest deshalb den einfachen Katalog. Dein Data Layer prüft dann pro Anfrage eine Berechtigung (can(user, "invoices.cancel")) statt eines Rollennamens. So funktioniert eine umbenannte oder neue Rolle ohne Codeänderung.
Wie geht es von hier aus weiter?
Wenn du nur Mitglieder, Einladungen und eine Rolle pro Person brauchst, starte mit der Members Table. Für den ganzen Team-Screen öffne die Demo der Users Admin Page: Probier einen Rollenwechsel, eine Änderung in der Matrix und die Filter des Audit-Logs aus und installier sie dann mit npx wingo-ui@latest add users-admin-page. Jede Demo und ihre Props sind ohne Konto öffentlich; den Quellcode dieser vier Teile gibt es mit Pro. Den Rest des Admin-Bereichs findest du in der Kategorie Praxis: Screens und Apps, darunter das Tutorial zur Settings-Seite, das dieselbe Save Bar nutzt.
Häufige Fragen
Was ist eine Role Management UI?
Das ist der Admin-Screen, auf dem der Owner eines Workspaces sieht, wer Zugriff hat, Leute einlädt, jeder Person eine Rolle gibt und festlegt, was jede Rolle darf. Die meisten B2B-Produkte zeigen auf demselben Screen auch die Historie dieser Änderungen in einem Audit-Log.
Sollte eine Berechtigungsmatrix jede Checkbox sofort speichern?
Meist nicht. Änderungen an Berechtigungen betreffen viele Leute auf einmal, also sammelst du sie in einem Entwurf, markierst die geänderten Zellen und speicherst alles zusammen über eine Save Bar mit Verwerfen daneben. Speichern pro Klick passt für die Rolle eines einzelnen Mitglieds, denn das ist eine klare Entscheidung.
Wie zeige ich eine Berechtigungsmatrix auf dem Smartphone?
Lass bei 390px kein Raster aus fünf Rollen seitwärts scrollen. Zeig jeweils eine Rolle, entweder als Akkordeon-Karte pro Rolle oder als Rollen-Chips über einer Liste von Switches, und fixier die Save Bar knapp über dem unteren Rand.
Reicht es, Buttons auszublenden, um Rollen durchzusetzen?
Nein. Die Oberfläche erklärt die Regeln nur. Jede Server Action und jede API-Route muss die Rolle des Aufrufers prüfen und die übermittelten Berechtigungen validieren, denn jeder kann die Anfrage auch ohne deine Buttons senden.
Zählen offene Einladungen als Seats?
Das sollten sie, bis sie ablaufen oder zurückgezogen werden. Sonst kann ein Admin mehr Einladungen verschicken, als der Plan erlaubt, und wer zuletzt annimmt, bekommt einen Fehler.
- RBAC
- Admin
- Next.js
- B2B SaaS
- Berechtigungen

