Skip to content

useTraceExplorer

useTraceExplorer manages the state for a paginated, filterable trace list. It handles data fetching, pagination, filtering, and optional auto-refresh. This is the same hook used internally by TraceExplorer.

Import

import { useTraceExplorer } from "@modernpath/agent-ui-react";

Options

interface UseTraceExplorerOptions {
  /** Backend instance for fetching trace data */
  backend: TraceBackend;

  /**
   * Number of traces per page.
   * @default 25
   */
  pageSize?: number;

  /**
   * Initial filter applied when the hook mounts.
   * @default {}
   */
  initialFilter?: TraceListFilter;

  /**
   * Auto-refresh interval in milliseconds.
   * Set to 0 or undefined to disable auto-refresh.
   */
  refreshInterval?: number;
}

Return Value

interface UseTraceExplorerReturn {
  /** Array of trace summaries for the current page */
  traces: TraceUI[];

  /** Total number of traces matching the current filter */
  total: number;

  /** Whether more pages are available beyond the current page */
  hasMore: boolean;

  /** Current page number (0-indexed) */
  page: number;

  /** Current active filter */
  filter: TraceListFilter;

  /** Update the filter. Resets pagination to page 0. */
  setFilter: (filter: TraceListFilter) => void;

  /** Reset the filter to the initial state. Resets pagination to page 0. */
  resetFilter: () => void;

  /** Navigate to a specific page */
  setPage: (page: number) => void;

  /** Whether a fetch is currently in progress */
  isLoading: boolean;

  /** Error from the last failed fetch, or null */
  error: Error | null;

  /** Manually trigger a refresh of the current page */
  refresh: () => void;
}

Behavior

Pagination

The hook fetches pageSize traces per page. Call setPage(n) to navigate to page n. The hasMore flag indicates whether additional pages exist.

Filtering

Call setFilter(filter) to apply a new filter. This resets the page to 0 and triggers a new fetch. Call resetFilter() to clear the filter back to the initial state.

Auto-Refresh

When refreshInterval is set to a positive number, the hook re-fetches the current page at that interval. The timer resets when the user changes the filter or page. Auto-refresh is paused while a fetch is in progress.

Cleanup

The hook cancels any in-flight requests and clears the auto-refresh timer when the component unmounts.

Usage

Basic Trace List

import {
  useTraceExplorer,
  createHttpTraceBackend,
} from "@modernpath/agent-ui-react";

const traceBackend = createHttpTraceBackend({ baseUrl: "/api" });

function CustomTraceList() {
  const {
    traces,
    total,
    hasMore,
    page,
    isLoading,
    error,
    setPage,
    refresh,
  } = useTraceExplorer({
    backend: traceBackend,
    pageSize: 20,
    refreshInterval: 30000,
  });

  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <button onClick={refresh} disabled={isLoading}>
        Refresh
      </button>
      <p>Total traces: {total}</p>

      <table>
        <thead>
          <tr>
            <th>Time</th>
            <th>Agent</th>
            <th>Status</th>
            <th>Duration</th>
          </tr>
        </thead>
        <tbody>
          {traces.map((trace) => (
            <tr key={trace.traceId}>
              <td>{new Date(trace.startTime).toLocaleString()}</td>
              <td>{trace.agent}</td>
              <td>{trace.status}</td>
              <td>{trace.durationMs}ms</td>
            </tr>
          ))}
        </tbody>
      </table>

      <div>
        <button onClick={() => setPage(page - 1)} disabled={page === 0}>
          Previous
        </button>
        <span>Page {page + 1}</span>
        <button onClick={() => setPage(page + 1)} disabled={!hasMore}>
          Next
        </button>
      </div>
    </div>
  );
}

With Filtering

import { useTraceExplorer, TraceListFilter } from "@modernpath/agent-ui-react";

function FilteredTraceList() {
  const {
    traces,
    filter,
    setFilter,
    resetFilter,
    isLoading,
  } = useTraceExplorer({
    backend: traceBackend,
    pageSize: 25,
    initialFilter: { status: "error" },
  });

  return (
    <div>
      <div style={{ display: "flex", gap: 8 }}>
        <select
          value={filter.status ?? ""}
          onChange={(e) =>
            setFilter({
              ...filter,
              status: (e.target.value || undefined) as any,
            })
          }
        >
          <option value="">All Statuses</option>
          <option value="ok">OK</option>
          <option value="error">Error</option>
          <option value="timeout">Timeout</option>
        </select>

        <input
          placeholder="Search prompts..."
          value={filter.search ?? ""}
          onChange={(e) =>
            setFilter({ ...filter, search: e.target.value || undefined })
          }
        />

        <button onClick={resetFilter}>Clear Filters</button>
      </div>

      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {traces.map((t) => (
            <li key={t.traceId}>
              {t.agent} - {t.status} - {t.durationMs}ms
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}