@bettercone/ui
ComponentsUsage

UsageDashboard

Complete usage tracking dashboard with API calls, storage, and feature access

UsageDashboard

The UsageDashboard is a composition component that provides a complete overview of organization usage metrics.

Plugin Required

This component requires the @bettercone/better-auth-plugin-usage-tracking plugin to be installed and configured in your Better Auth setup. See Plugin Installation below.

This is a composition component that combines ApiUsageCard and FeatureAccessCard. You provide the data from your backend.

Plugin Installation

First, install the usage tracking plugin:

pnpm add @bettercone/better-auth-plugin-usage-tracking

Then configure it in your Better Auth setup:

lib/auth.ts
import { betterAuth } from "better-auth";
import { usageTracking } from "@bettercone/better-auth-plugin-usage-tracking";

export const auth = betterAuth({
  // ... other config
  plugins: [
    usageTracking(),
  ],
});

Installation

import { UsageDashboard } from '@bettercone/ui';

Features

  • Backend-Agnostic - Works with any Better Auth backend (Convex, Prisma, Supabase, Drizzle)
  • API Usage Tracking - Monitor API calls against plan limits
  • Storage Monitoring - Track storage consumption with visual indicators
  • Feature Access - Display available features by plan
  • Flexible Layout - Grid or stack layout options
  • Upgrade Prompts - Smart prompts when approaching limits via callbacks
  • Progress Visualization - Clear progress bars and percentages
  • Loading States - Built-in skeleton loading
  • Type-Safe - Full TypeScript support

Usage

import { UsageDashboard } from '@bettercone/ui';
import { useRouter } from 'next/navigation';

export default function UsagePage() {
  const router = useRouter();

  return (
    <div className="container mx-auto py-8">
      <h1 className="text-3xl font-bold mb-6">Usage & Limits</h1>
      <UsageDashboard
        apiUsage={{ current: 8500, limit: 10000 }}
        featureAccess={{
          planId: "pro",
          advancedAnalytics: true,
          apiAccess: true,
          customIntegrations: false
        }}
        apiUsageCardProps={{
          onUpgrade: () => router.push('/pricing')
        }}
        featureAccessCardProps={{
          onUpgrade: () => router.push('/pricing')
        }}
      />
    </div>
  );
}
import { UsageDashboard } from '@bettercone/ui';
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';
import { authClient } from '@/lib/auth-client';
import { useRouter } from 'next/navigation';

export default function UsagePage() {
  const router = useRouter();
  const { data: session } = authClient.useSession();
  const { data: organizations } = authClient.useListOrganizations();
  const activeOrg = organizations?.[0];

  const usageData = useQuery(
    api.usage.getCurrentUsage,
    session?.user?.id
      ? { userId: session.user.id, organizationId: activeOrg?.id }
      : "skip"
  );

  const featureAccess = useQuery(
    api.usage.getFeatureAccess,
    session?.user?.id
      ? { userId: session.user.id, organizationId: activeOrg?.id }
      : "skip"
  );

  return (
    <div className="container mx-auto py-8 px-4">
      <div className="mb-8">
        <h1 className="text-3xl font-bold mb-2">Usage Metrics</h1>
        <p className="text-muted-foreground">
          Monitor your usage across all metrics
        </p>
      </div>
      
      <div className="max-w-4xl mx-auto">
        <UsageDashboard />
      </div>
    </div>
  );
}
import { UsageDashboard } from '@/components';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';

export default function DashboardPage() {
  return (
    <div className="container mx-auto py-8">
      <Tabs defaultValue="overview">
        <TabsList>
          <TabsTrigger value="overview">Overview</TabsTrigger>
          <TabsTrigger value="usage">Usage</TabsTrigger>
          <TabsTrigger value="billing">Billing</TabsTrigger>
        </TabsList>
        
        <TabsContent value="usage" className="mt-6">
          <UsageDashboard />
        </TabsContent>
      </Tabs>
    </div>
  );
}
import { UsageDashboard } from '@/components';
import { Suspense } from 'react';
import { Skeleton } from '@/components/ui/skeleton';

function UsageDashboardSkeleton() {
  return (
    <div className="space-y-6">
      <Skeleton className="h-48 w-full" />
      <Skeleton className="h-48 w-full" />
      <Skeleton className="h-64 w-full" />
    </div>
  );
}

export default function UsagePage() {
  return (
    <div className="container mx-auto py-8">
      <h1 className="text-3xl font-bold mb-6">Usage</h1>
      
      <Suspense fallback={<UsageDashboardSkeleton />}>
        <UsageDashboard />
      </Suspense>
    </div>
  );
}

Props

The UsageDashboard component has no required props - it automatically fetches and displays usage data for the authenticated user.

Prop

Type

The component automatically handles authentication, organization context, and data fetching internally.

Usage Metrics

1. API Usage

Tracks API calls made against your plan's limit:

  • Current Usage: Number of API calls this billing period
  • Limit: Maximum API calls allowed by plan
  • Percentage: Visual progress indicator
  • Reset Date: When the counter resets (monthly)

Example Plans:

  • Free: 10,000 API calls/month
  • Pro: 100,000 API calls/month
  • Team: Unlimited

2. Storage Usage

Monitors file and data storage consumption:

  • Current Storage: Used storage in GB/MB
  • Limit: Maximum storage allowed by plan
  • Percentage: Visual progress indicator
  • File Count: Number of stored files (optional)

Example Plans:

  • Free: 1 GB
  • Pro: 10 GB
  • Team: 100 GB

3. Feature Access

Displays available features by subscription plan:

  • Available Features: Green checkmarks for enabled features
  • Locked Features: Locked icons for premium features
  • Upgrade Prompts: Contextual upgrade suggestions

Example Features:

  • Advanced Analytics
  • Priority Support
  • Custom Domains
  • API Access
  • Team Collaboration
  • White Labeling

Examples

Example 1: Protected Usage Page

'use client';

import { UsageDashboard } from '@/components';
import { SignedIn, SignedOut } from '@daveyplate/better-auth-ui';
import { Button } from '@/components/ui/button';
import Link from 'next/link';

export default function UsagePage() {
  return (
    <div className="container mx-auto py-8 px-4">
      <SignedOut>
        <div className="text-center py-12">
          <h1 className="text-2xl font-bold mb-4">Sign in to view usage</h1>
          <Button asChild>
            <Link href="/auth/sign-in">Sign In</Link>
          </Button>
        </div>
      </SignedOut>

      <SignedIn>
        <div className="mb-8">
          <h1 className="text-3xl font-bold mb-2">Usage & Limits</h1>
          <p className="text-muted-foreground">
            Track your API usage, storage, and available features
          </p>
        </div>

        <UsageDashboard />
        
        <div className="mt-8 p-6 bg-muted rounded-lg">
          <h2 className="font-semibold mb-2">Need More Resources?</h2>
          <p className="text-sm text-muted-foreground mb-4">
            Upgrade your plan to get higher limits and access to premium features.
          </p>
          <Link 
            href="/pricing" 
            className="text-sm font-medium text-primary hover:underline"
          >
            View Pricing Plans →
          </Link>
        </div>
      </SignedIn>
    </div>
  );
}

Example 2: Dashboard Widget

import { UsageDashboard } from '@/components';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';

export default function DashboardPage() {
  return (
    <div className="container mx-auto py-8">
      <div className="grid gap-6 md:grid-cols-2">
        {/* Other dashboard widgets */}
        <Card>
          <CardHeader>
            <CardTitle>Quick Stats</CardTitle>
          </CardHeader>
          <CardContent>
            {/* Stats content */}
          </CardContent>
        </Card>
        
        {/* Usage widget */}
        <Card className="md:col-span-2">
          <CardHeader>
            <CardTitle>Usage Metrics</CardTitle>
          </CardHeader>
          <CardContent>
            <UsageDashboard />
          </CardContent>
        </Card>
      </div>
    </div>
  );
}

Example 3: Usage with Alerts

import { UsageDashboard } from '@/components';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { AlertTriangle } from 'lucide-react';
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';

export default function UsagePage() {
  // Check if user is near limits
  const usageData = useQuery(api.usage.getCurrentUsage, {
    userId: session?.user?.id,
  });

  const nearLimit = usageData && (
    usageData.apiCalls / usageData.apiLimit > 0.8 ||
    usageData.storageBytes / usageData.storageLimit > 0.8
  );

  return (
    <div className="container mx-auto py-8 px-4">
      <h1 className="text-3xl font-bold mb-6">Usage</h1>
      
      {nearLimit && (
        <Alert className="mb-6">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Approaching Limit</AlertTitle>
          <AlertDescription>
            You're using over 80% of your plan limits. 
            Consider upgrading to avoid service interruption.
          </AlertDescription>
        </Alert>
      )}
      
      <UsageDashboard />
    </div>
  );
}

Example 4: Admin Usage Overview

import { UsageDashboard } from '@/components';
import { 
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue 
} from '@/components/ui/select';
import { useState } from 'react';

export default function AdminUsagePage() {
  const [selectedOrg, setSelectedOrg] = useState<string>('all');

  return (
    <div className="container mx-auto py-8 px-4">
      <div className="flex items-center justify-between mb-8">
        <h1 className="text-3xl font-bold">Organization Usage</h1>
        
        <Select value={selectedOrg} onValueChange={setSelectedOrg}>
          <SelectTrigger className="w-64">
            <SelectValue placeholder="Select organization" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="all">All Organizations</SelectItem>
            <SelectItem value="org1">Acme Corp</SelectItem>
            <SelectItem value="org2">TechStart Inc</SelectItem>
          </SelectContent>
        </Select>
      </div>
      
      <UsageDashboard />
    </div>
  );
}

Component Composition

The UsageDashboard is composed of two main cards:

  1. ApiUsageCard - API call tracking
  2. FeatureAccessCard - Feature availability

You can use these cards individually for custom layouts:

import { 
  ApiUsageCard,
  FeatureAccessCard 
} from '@bettercone/ui';

export default function CustomUsagePage() {
  return (
    <div className="grid gap-6 lg:grid-cols-2">
      {/* Side by side layout */}
      <ApiUsageCard 
        current={8500}
        limit={10000}
        onUpgrade={() => router.push('/pricing')}
      />
      
      
      <FeatureAccessCard 
          features={featureList}
          onUpgrade={() => router.push('/pricing')}
        />
      </div>
    </div>
  );
}

Real-Time Updates

The component uses Convex for real-time data synchronization:

// Usage updates automatically when:
// 1. User makes API calls
// 2. Files are uploaded/deleted
// 3. Plan is upgraded/downgraded
// 4. Billing period resets

// No manual refresh needed!

Usage data updates in real-time across all user sessions thanks to Convex's reactive queries.

Tracking Usage in Your App

To track API usage in your application:

// In your API route or server action
import { api } from '@/convex/_generated/api';
import { fetchMutation } from 'convex/nextjs';

export async function POST(request: Request) {
  // Your API logic...
  
  // Track the API call
  await fetchMutation(api.usage.trackApiCall, {
    userId: session.user.id,
    endpoint: '/api/data',
    organizationId: org?.id,
  });
  
  return Response.json({ success: true });
}

Upgrade Flow

When users approach or exceed limits:

  1. Warning at 80%: Visual indicator changes to warning color
  2. Alert at 90%: Prominent upgrade prompt appears
  3. Blocked at 100%: API calls are rate-limited, upgrade required
// Automatic upgrade prompts
<UsageDashboard />
// Shows "Upgrade Plan" button when limits approached

Plan Limits Configuration

Configure plan limits in packages/convex/convex/subscriptionPlans.ts:

export const subscriptionPlans = [
  {
    id: "free",
    name: "Free",
    limits: {
      apiCalls: 10000,      // 10k API calls/month
      storage: 1048576,     // 1 GB in KB
      features: ["basic"],
    },
  },
  {
    id: "pro",
    name: "Pro",
    limits: {
      apiCalls: 100000,     // 100k API calls/month
      storage: 10485760,    // 10 GB in KB
      features: ["basic", "advanced", "analytics"],
    },
  },
  // More plans...
];

Notes

Rate Limiting: API usage tracking is essential for implementing rate limiting. Ensure all API endpoints track usage properly.

  • Usage resets monthly on subscription renewal date
  • Storage includes all uploaded files and database storage
  • Feature access is determined by active subscription plan
  • Organization-level usage aggregates all member activity
  • Exceeded limits may result in service throttling

Next Steps