mdxui

Usage

API usage metrics and limits view component for DeveloperApp

Usage

The Usage view provides comprehensive API usage analytics, rate limiting information, quota tracking, and billing insights. Monitor API consumption patterns and optimize usage across endpoints and time periods.

Type Signature

interface UsageViewProps {
  /** Page title */
  title?: string
  /** Page description */
  description?: string
  /** View layout mode */
  layout?: 'single' | 'column' | 'canvas' | 'vibe'
  /** Usage metrics */
  metrics?: {
    requests: number // Total requests
    quota?: number // Usage quota limit
    bandwidth?: number // Bandwidth used (bytes)
    errors?: number // Error count
  }
  /** Usage over time */
  timeseries?: Array<{
    timestamp: string
    requests: number
    errors?: number
  }>
  /** Rate limit info */
  rateLimit?: {
    limit: number
    remaining: number
    reset: string // Reset timestamp
  }
  /** Group by */
  groupBy?: 'total' | 'endpoint' | 'key' | 'status'
  /** Page-level action buttons */
  actions?: Array<{
    id: string
    label: string
    icon?: string
    onClick?: () => void
    variant?: 'default' | 'primary' | 'destructive'
  }>
  /** Child content */
  children?: React.ReactNode
}

Usage

<App name="Developer Portal">
  <Shell>
    <UsageView
      title="API Usage"
      description="Monitor API consumption and rate limits"
      groupBy="endpoint"
    />
  </Shell>
</App>

Props

title

The page title displayed in the header. Defaults to "Usage".

<UsageView title="API Usage">

description

Optional page description or subtitle.

<UsageView
  title="API Usage"
  description="Track your API consumption, quotas, and rate limits"
>

layout

Layout mode for the view content. Defaults to 'single'.

// Single column layout (default)
<UsageView layout="single">

// Multi-column layout
<UsageView layout="column">

metrics

Current usage statistics and totals.

<UsageView
  metrics={{
    requests: 1245678,
    quota: 2000000,
    bandwidth: 45231987654, // bytes
    errors: 234
  }}
>

timeseries

Usage data over time for charts and trends.

<UsageView
  timeseries={[
    {
      timestamp: '2024-12-19T00:00:00Z',
      requests: 12450,
      errors: 23
    },
    {
      timestamp: '2024-12-19T01:00:00Z',
      requests: 11230,
      errors: 15
    }
    // ... more data points
  ]}
>

rateLimit

Current rate limit status.

<UsageView
  rateLimit={{
    limit: 1000,
    remaining: 847,
    reset: '2024-12-19T09:00:00Z'
  }}
>

groupBy

How to group and display usage data. Defaults to 'total'.

// Total usage (default)
<UsageView groupBy="total">

// By API endpoint
<UsageView groupBy="endpoint">

// By API key
<UsageView groupBy="key">

// By HTTP status code
<UsageView groupBy="status">

actions

Page-level action buttons displayed in the header.

<UsageView
  actions={[
    {
      id: 'export',
      label: 'Export Data',
      icon: 'Download',
      onClick: () => exportUsageData()
    },
    {
      id: 'upgrade',
      label: 'Upgrade Plan',
      icon: 'Zap',
      variant: 'primary',
      onClick: () => navigateToUpgrade()
    }
  ]}
>

Usage Metrics

Overview Metrics

Display key usage statistics:

<UsageView
  metrics={{
    requests: 1245678,
    quota: 2000000,
    bandwidth: 45231987654,
    errors: 234
  }}
>
  <div className="grid grid-cols-4 gap-4">
    <MetricCard
      title="Requests"
      value={formatNumber(metrics.requests)}
      description={`${percentage(metrics.requests, metrics.quota)}% of quota`}
      trend="up"
    />
    <MetricCard
      title="Quota"
      value={`${formatNumber(metrics.requests)} / ${formatNumber(metrics.quota)}`}
      description="Monthly limit"
    />
    <MetricCard
      title="Bandwidth"
      value={formatBytes(metrics.bandwidth)}
      description="Data transferred"
    />
    <MetricCard
      title="Error Rate"
      value={`${percentage(metrics.errors, metrics.requests)}%`}
      description={`${formatNumber(metrics.errors)} errors`}
      trend="down"
    />
  </div>
</UsageView>

Rate Limit Status

Show current rate limit information:

<UsageView rateLimit={rateLimit}>
  <Card>
    <CardHeader>
      <CardTitle>Rate Limit</CardTitle>
      <CardDescription>
        Resets {formatRelativeTime(rateLimit.reset)}
      </CardDescription>
    </CardHeader>
    <CardContent>
      <div className="space-y-2">
        <div className="flex justify-between text-sm">
          <span>Used</span>
          <span>{rateLimit.limit - rateLimit.remaining} / {rateLimit.limit}</span>
        </div>
        <Progress
          value={(rateLimit.limit - rateLimit.remaining) / rateLimit.limit * 100}
        />
        <div className="text-sm text-muted-foreground">
          {rateLimit.remaining} requests remaining
        </div>
      </div>
    </CardContent>
  </Card>
</UsageView>

Time Series Charts

Visualize usage over time:

<UsageView timeseries={timeseries}>
  <Card>
    <CardHeader>
      <CardTitle>Requests Over Time</CardTitle>
      <CardDescription>Last 24 hours</CardDescription>
    </CardHeader>
    <CardContent>
      <LineChart
        data={timeseries}
        xAxis={{ dataKey: 'timestamp', tickFormatter: formatTime }}
        yAxis={{ tickFormatter: formatNumber }}
        lines={[
          { dataKey: 'requests', stroke: '#3b82f6', name: 'Requests' },
          { dataKey: 'errors', stroke: '#ef4444', name: 'Errors' }
        ]}
      />
    </CardContent>
  </Card>
</UsageView>

Breakdown Views

By Endpoint

Show usage grouped by API endpoint:

<UsageView groupBy="endpoint">
  <Table>
    <TableHeader>
      <TableRow>
        <TableHead>Endpoint</TableHead>
        <TableHead>Requests</TableHead>
        <TableHead>Avg Response Time</TableHead>
        <TableHead>Error Rate</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      {endpointUsage.map(endpoint => (
        <TableRow key={endpoint.path}>
          <TableCell>
            <Code>{endpoint.method} {endpoint.path}</Code>
          </TableCell>
          <TableCell>{formatNumber(endpoint.requests)}</TableCell>
          <TableCell>{endpoint.avgResponseTime}ms</TableCell>
          <TableCell>
            <Badge variant={endpoint.errorRate > 5 ? 'destructive' : 'secondary'}>
              {endpoint.errorRate}%
            </Badge>
          </TableCell>
        </TableRow>
      ))}
    </TableBody>
  </Table>
</UsageView>

By API Key

Track usage per API key:

<UsageView groupBy="key">
  <div className="space-y-4">
    {keyUsage.map(key => (
      <Card key={key.id}>
        <CardHeader>
          <CardTitle>{key.name}</CardTitle>
          <CardDescription>{key.key}</CardDescription>
        </CardHeader>
        <CardContent>
          <div className="grid grid-cols-3 gap-4">
            <div>
              <div className="text-sm text-muted-foreground">Requests</div>
              <div className="text-2xl font-bold">{formatNumber(key.requests)}</div>
            </div>
            <div>
              <div className="text-sm text-muted-foreground">Bandwidth</div>
              <div className="text-2xl font-bold">{formatBytes(key.bandwidth)}</div>
            </div>
            <div>
              <div className="text-sm text-muted-foreground">Error Rate</div>
              <div className="text-2xl font-bold">{key.errorRate}%</div>
            </div>
          </div>
        </CardContent>
      </Card>
    ))}
  </div>
</UsageView>

By Status Code

Analyze HTTP status code distribution:

<UsageView groupBy="status">
  <div className="grid grid-cols-2 gap-4">
    <Card>
      <CardHeader>
        <CardTitle>Status Code Distribution</CardTitle>
      </CardHeader>
      <CardContent>
        <PieChart
          data={[
            { name: '2xx Success', value: statusCodes['2xx'], fill: '#10b981' },
            { name: '4xx Client Error', value: statusCodes['4xx'], fill: '#f59e0b' },
            { name: '5xx Server Error', value: statusCodes['5xx'], fill: '#ef4444' }
          ]}
        />
      </CardContent>
    </Card>

    <Card>
      <CardHeader>
        <CardTitle>Status Breakdown</CardTitle>
      </CardHeader>
      <CardContent>
        <Table>
          <TableBody>
            {Object.entries(statusCodes).map(([code, count]) => (
              <TableRow key={code}>
                <TableCell>{code}</TableCell>
                <TableCell>{formatNumber(count)}</TableCell>
                <TableCell>{percentage(count, totalRequests)}%</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </CardContent>
    </Card>
  </div>
</UsageView>

Time Period Filters

Filter usage by time period:

const [period, setPeriod] = useState<TimePeriod>('24h')

<UsageView
  actions={[
    {
      id: 'period',
      label: period,
      onClick: () => showPeriodMenu()
    }
  ]}
>
  <Select value={period} onValueChange={setPeriod}>
    <SelectTrigger>
      <SelectValue />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="1h">Last hour</SelectItem>
      <SelectItem value="24h">Last 24 hours</SelectItem>
      <SelectItem value="7d">Last 7 days</SelectItem>
      <SelectItem value="30d">Last 30 days</SelectItem>
      <SelectItem value="custom">Custom range</SelectItem>
    </SelectContent>
  </Select>

  <UsageChart period={period} />
</UsageView>

Quota Management

Display quota usage and upgrade options:

<UsageView>
  <Card>
    <CardHeader>
      <CardTitle>Usage Quota</CardTitle>
      <CardDescription>Monthly API request limit</CardDescription>
    </CardHeader>
    <CardContent>
      <div className="space-y-4">
        <div>
          <div className="flex justify-between mb-2">
            <span>{formatNumber(used)} used</span>
            <span>{formatNumber(quota)} limit</span>
          </div>
          <Progress value={usedPercentage} />
        </div>

        {usedPercentage > 80 && (
          <Alert variant="warning">
            <AlertTitle>Approaching quota limit</AlertTitle>
            <AlertDescription>
              You've used {usedPercentage}% of your monthly quota.
              Consider upgrading to a higher plan.
            </AlertDescription>
          </Alert>
        )}

        <Button onClick={() => navigateToUpgrade()}>
          Upgrade Plan
        </Button>
      </div>
    </CardContent>
  </Card>
</UsageView>

Export Functionality

Allow users to export usage data:

const handleExport = async (format: 'csv' | 'json') => {
  const data = await exportUsageData({ format, period })
  downloadFile(data, `usage-${period}.${format}`)
  toast.success(`Usage data exported as ${format.toUpperCase()}`)
}

<UsageView
  actions={[
    {
      id: 'export',
      label: 'Export',
      icon: 'Download',
      onClick: () => showExportDialog()
    }
  ]}
>
  <ExportDialog
    formats={['csv', 'json']}
    onExport={handleExport}
  />
</UsageView>

Real-Time Updates

Show live usage metrics:

<UsageView>
  <Card>
    <CardHeader>
      <div className="flex items-center justify-between">
        <CardTitle>Live Requests</CardTitle>
        <Badge variant="success">
          <span className="animate-pulse">ā—</span> Live
        </Badge>
      </div>
    </CardHeader>
    <CardContent>
      <RealtimeChart
        data={realtimeData}
        interval={1000} // Update every second
      />
    </CardContent>
  </Card>
</UsageView>

Billing Integration

Link usage to billing information:

<UsageView>
  <Card>
    <CardHeader>
      <CardTitle>Usage & Billing</CardTitle>
      <CardDescription>Current billing period</CardDescription>
    </CardHeader>
    <CardContent>
      <div className="space-y-4">
        <div className="flex justify-between">
          <span>Base plan</span>
          <span>$49/month</span>
        </div>
        <div className="flex justify-between">
          <span>Additional requests</span>
          <span>${calculateOverage(usage)} ({formatNumber(overage)} requests)</span>
        </div>
        <Separator />
        <div className="flex justify-between font-bold">
          <span>Total</span>
          <span>${calculateTotal(usage)}</span>
        </div>
      </div>
    </CardContent>
    <CardFooter>
      <Button onClick={() => navigateToBilling()}>
        View Billing
      </Button>
    </CardFooter>
  </Card>
</UsageView>

Empty State

Handle cases with no usage data:

<UsageView>
  {totalRequests === 0 ? (
    <EmptyState
      icon="BarChart"
      title="No usage data yet"
      description="Start making API requests to see usage metrics and analytics"
      action={{
        label: 'View API Docs',
        onClick: () => navigateToDocs()
      }}
    />
  ) : (
    <UsageCharts data={usageData} />
  )}
</UsageView>

Integration with Payload CMS

Auto-wire to Payload usage collection:

// Auto-fetches from Payload 'usage' collection
<UsageView collection="usage">
  {/* Usage data loaded automatically */}
</UsageView>

DeveloperApp Integration

Part of the DeveloperApp component suite:

<DeveloperDashboard
  config={{
    showAPIKeys: true,
    showWebhooks: true,
    showUsage: true,
    showLogs: true
  }}
>
  <APIKeysView />
  <WebhooksView />
  <UsageView />
  <LogsView />
</DeveloperDashboard>

Responsive Behavior

The view adapts to screen size:

Desktop (≄1024px)

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Usage                       [Export]  │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│  ā”Œā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”      │
│  │1.2M │ │62.4%│ │45GB │ │0.02%│      │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”˜      │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │
│  │      [Usage Chart]               │  │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │
│  │  Endpoint Breakdown Table        │  │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
Metrics + charts + breakdown

Mobile (under 768px)

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Usage            [Export] │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”    │
│  │ 1.2M Requests      │    │
│  │ 62.4% of quota     │    │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜    │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”    │
│  │  [Chart]           │    │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
Stacked metrics and chart

Accessibility

The Usage view implements accessibility features:

  • Keyboard navigation - Tab through filters and actions
  • Screen reader labels - Descriptive labels for metrics
  • Chart accessibility - Data tables for screen readers
  • Status announcements - Announce quota warnings
<UsageView aria-label="API usage metrics and analytics">
  <MetricCard
    aria-label={`${formatNumber(requests)} API requests, ${percentage(requests, quota)}% of quota`}
  />

  <Chart
    aria-label="API requests over time"
    aria-describedby="chart-description"
  >
    <span id="chart-description" className="sr-only">
      Line chart showing API request volume over the last 24 hours
    </span>
  </Chart>
</UsageView>

On this page