ComponentsData display

Audit log

The full ledger of who did what, when and from where — sign-ins, permission changes, exports, deletes. Filter by text, actor, action and severity, expand an event for its field diff, JSON diff and raw payload, and scroll thousands of rows: long logs are virtualized while keeping native table semantics.

@tanstack/react-virtual + native <table> (one <tbody> per event) components/audit-log.tsx, components/version-history.tsx, styles/data.csstheme · material · accent · density · font
8 eventsShowing the last 90 days · times in UTC
DetailsTimeActorActionTargetSource
Ayşe KayaUserChanged roleWarninguser.role.updatedDaniel BrooksUser · usr_48288.230.14.7Istanbul, TR
Mehmet YılmazUserDisabled two-factorCriticalauth.mfa.disabledMehmet YılmazUser · usr_311176.88.40.211Ankara, TR
Zapier syncAPI keyCreated contactcontact.createdJane CooperContact · con_120954.86.50.139Ashburn, US
Daniel BrooksUserUpdated dealdeal.updatedNorthwind renewal 2027Deal · deal_11888.230.14.9Istanbul, TR
Nightly exportSystemExported dataWarningexport.createdAll contacts (CSV · 12,408 rows)Export · exp_77—
İlker ŞahinUserDeleted postpost.deletedSummer menu launch (draft)Post · post_7795.70.180.3Izmir, TR
UnknownSystemFailed sign-inCriticalauth.login.faileddaniel.brooks@vamos.istUser · usr_482185.220.101.44Frankfurt, DE
Ayşe KayaUserCreated API keyapiKey.createdZapier syncApiKey · key_zap_0288.230.14.7Istanbul, TR

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { AuditLog } from "@vamos-ui/react";

<AuditLog
  events={events}
  actionLabels={{ "user.role.updated": "Changed role", "export.created": "Exported data" }}
  notice="Showing the last 90 days"
  hasMore={hasMore}
  onLoadMore={fetchOlder}
/>

Rows expand with a labelled button (aria-expanded + aria-controls). Raw JSON sits in a focusable <pre> with a copy button. No row, header or tooltip is blurred in glass mode.

Examples

Event detail

Field or JSON diff of the change, ids with copy buttons, client, and the raw event.

Changes

FieldBeforeAfter
RoleChangedEditorAdmin
PermissionsChangedposts.writeposts.write, users.manage, billing.view

Details

Event ID
evt_8f21a0
Request ID
req_9f2c41d7
IP address
88.230.14.7
Client
Chrome 131 on macOS 15
Reason
Covering for team lead during leave

Server mode, 2,000 events

manualFiltering + onFiltersChange for your API; rowCount gives "250 of 2,000"; scrolling near the end calls onLoadMore.

250 of 2,000 eventsVirtualized · loads 250 at a time
DetailsTimeActorActionTargetSource

One record's history

variant="timeline" renders into Activity timeline with the field diff as the body.

  1. Ayşe Kaya changed role · Daniel Brooks
    FieldBeforeAfter
    RoleChangedEditorAdmin
    PermissionsChangedposts.writeposts.write, users.manage, billing.view
  2. Unknown failed sign-in · daniel.brooks@vamos.ist

API

AuditLog

PropTypeDefault
events
Events, newest first.
AuditEvent[]—
actionLabels
"user.role.updated" → "Changed role". Missing codes are humanized.
Record<string, string>—
changeFields
Labels and formats for the change diff.
FieldDiffField[]—
filters / defaultFilters / onFiltersChange
{ q, actorId, action, severity } — controlled or not.
AuditFilters / … / (filters) => void—
manualFiltering
Server mode: events are already filtered.
booleanfalse
rowCount
Server total for "50 of 12,408 events".
number—
showFilters
Built-in filter row.
booleantrue
toolbar
Extra filters (a Date range picker).
ReactNode—
notice
Retention or scope notice.
ReactNode—
onExport
Export button with the visible events.
(events) => void—
hasMore / onLoadMore
Infinite scroll plus a "Load older events" button.
boolean / () => void | Promise<void>—
loading
Busy indicator in the summary.
booleanfalse
height
Scroll viewport height.
string"32rem"
virtualizeAfter
Virtualize above this many events.
number80
variant
Full ledger or a compact record feed.
"table" | "timeline""table"
labels
All built-in strings.
Partial<AuditLogLabels>—

AuditEvent

PropTypeDefault
id / at
Identity and time.
string / ISO instant—
actor
Who did it.
{ id; name; type: "user" | "api-key" | "system"; avatar? }—
action
Dotted code; filtering by "user" matches "user.*".
string—
target
What it was done to.
{ type; id; label? }—
ip / location / userAgent
Where from (mono, left-to-right).
string—
changes
Field diff in the detail.
{ before; after }—
metadata
requestId gets a copy row; the rest is listed.
Record<string, unknown>—
severity
Badge and edge marker.
"info" | "warning" | "critical""info"

Parts and helpers

PropTypeDefault
AuditEventDetail
The expanded content on its own (drawer, page).
{ event, changeFields }—
filterAuditEvents(events, filters, locale?, actionLabels?)
The local filter, also usable on the server.
AuditEvent[]—