Timeline Component

The Timeline component is a ready-made UI that displays audit events in a beautiful, easy-to-read timeline format. It handles fetching events, pagination, filtering, and all the complexity of displaying audit data.

What It Does

The Timeline component automatically:

  • Fetches short-lived frontend tokens when needed
  • Calls the Archiva API directly (no proxy routes needed)
  • Handles token refresh automatically (tokens expire after 90 seconds)
  • Retries on authentication errors (401/403)
  • Manages loading states and error handling
  • Provides pagination with a "Load more" button
  • Displays events in a clean, readable format

Basic Usage

'use client';

import { Timeline } from '@archiva/archiva-nextjs/react/client';

export default function InvoicePage({ params }: { params: { id: string } }) {
  return (
    <div>
      <h1>Invoice {params.id}</h1>
      <Timeline
        entityId={params.id}
        entityType="invoice"
        initialLimit={25}
      />
    </div>
  );
}

What this shows: A timeline of all events related to this specific invoice. Users can see who did what, when, and scroll through the history.

Props

Filtering Props

These props filter which events are displayed. They're applied as base filters and cannot be overridden by users.

PropTypeRequiredDescription
entityIdstringNoFilter events by entity ID (e.g., a specific invoice ID)
tenantIdstringNoFilter events by tenant ID (for multi-tenant applications)
actorIdstringNoFilter events by actor ID (e.g., a specific user)
entityTypestringNoFilter events by entity type (e.g., "invoice", "user", "order")
actionKeystringNoFilter events by action key (e.g., "invoice.update")
actionDescriptionstringNoFilter events by action description (client-side only)
actorType'user' | 'service' | 'system' | ('user' | 'service' | 'system')[]NoFilter events by actor type(s)

Why these are base filters: When you set entityId and entityType, you're saying "show me events for this specific invoice." Users can't override this to see other invoices—it's locked to what you specify.

Display Options

PropTypeRequiredDefaultDescription
initialLimitnumberNo100Number of events to fetch initially
classNamestringNo-Additional CSS class names for styling
emptyMessagestringNo'No events yet.'Message to display when no events are found
showSearchbooleanNofalseShow search input for filtering events by text
showFiltersbooleanNofalseShow filter inputs for advanced filtering
showSystemAndServicesbooleanNofalseInclude system and service actors in results (defaults to user-only)

Customization

PropTypeRequiredDescription
getActorAvatar(actorId: string) => string | React.ComponentType<{ className?: string }> | undefinedNoCustom function to get actor avatar URL or icon component

Examples

Basic Timeline

Show all events for a specific entity:

'use client';

import { Timeline } from '@archiva/archiva-nextjs/react/client';

export function InvoiceActivityLog({ invoiceId }: { invoiceId: string }) {
  return (
    <Timeline
      entityId={invoiceId}
      entityType="invoice"
    />
  );
}

With Search and Filters

Enable user-controlled search and filtering:

'use client';

import { Timeline } from '@archiva/archiva-nextjs/react/client';

export function AdminAuditLog() {
  return (
    <Timeline
      showSearch={true}
      showFilters={true}
      showSystemAndServices={true}
      initialLimit={50}
    />
  );
}

Custom Actor Avatars

Provide custom avatars for actors:

'use client';

import { Timeline } from '@archiva/archiva-nextjs/react/client';

export function UserActivityFeed({ userId }: { userId: string }) {
  return (
    <Timeline
      actorId={userId}
      getActorAvatar={(actorId) => {
        // Return avatar URL for users
        if (actorId.startsWith('user:')) {
          const userId = actorId.split(':')[1];
          return `https://api.example.com/avatars/${userId}`;
        }
        // Return undefined to use default (Gravatar for users, icons for system/service)
        return undefined;
      }}
    />
  );
}

Tenant-Scoped Timeline

Show events for a specific tenant:

'use client';

import { Timeline } from '@archiva/archiva-nextjs/react/client';

export function OrganizationActivityLog({ orgId }: { orgId: string }) {
  return (
    <Timeline
      tenantId={orgId}
      showSearch={true}
      initialLimit={25}
    />
  );
}

User Actions Only

Show only actions taken by users (exclude automated system actions):

'use client';

import { Timeline } from '@archiva/archiva-nextjs/react/client';

export function UserFacingActivityFeed({ entityId }: { entityId: string }) {
  return (
    <Timeline
      entityId={entityId}
      actorType="user"
      showSearch={true}
    />
  );
}

Features

Automatic Pagination

The Timeline component automatically handles pagination. When there are more events to load, a "Load more" button appears. Clicking it fetches the next page of events and appends them to the timeline.

Why this matters: You don't need to implement pagination logic yourself. The component handles cursor-based pagination automatically, which remains fast even with millions of events.

Search

When showSearch={true}, users can search across:

  • Action keys (e.g., "invoice.update")
  • Action descriptions
  • Entity types
  • Entity IDs
  • Actor IDs
  • Actor display names
  • Source identifiers

Note: Search is client-side, so it works on the events that have already been loaded. For server-side search, use the q parameter in the List Events API directly.

Filtering

When showFilters={true}, users can filter by:

  • Tenant ID
  • Action key
  • Entity ID
  • Entity type
  • Actor ID
  • Actor type

Important: Filters set via props (like entityId or entityType) are locked and cannot be overridden by users. This ensures users can only see events you want them to see.

Actor Display

The component automatically:

  • Shows user avatars (defaults to Gravatar if no custom avatar provided)
  • Displays icons for system and service actors
  • Shows actor display names when available
  • Falls back to actor IDs when display names aren't available

Error Handling

The component gracefully handles:

  • Network errors
  • Authentication failures (automatically retries with token refresh)
  • Empty result sets
  • Loading states

Styling

The Timeline component uses inline styles by default for a clean, modern look. You can customize it by:

  1. Adding a className: Pass a className prop to apply your own CSS
  2. Using CSS overrides: Target the component's internal elements with CSS selectors
  3. Wrapping in a styled container: Wrap the component in your own styled div

Import Path

Important: Always import from the client entrypoint:

import { Timeline } from '@archiva/archiva-nextjs/react/client';

Do NOT import from the root package or /react in client components, as this will cause Next.js React Server Component errors.

Requirements

  • Must be used within an ArchivaProvider (see ArchivaProvider)
  • Must be in a client component (use 'use client' directive)
  • Requires the frontend token route to be set up (see Frontend Token Route)

Next Steps