# Workflow runs

> The latest workflow runs with their status, which update while a run is in progress.

Source: https://bettersupabase.com/docs/registry/ui-recipes/workflow-runs

{/* Generated by scripts/build-recipes.ts from the example apps. Edit the examples or the script, then run `pnpm recipes`. */}

It calls `useWorkflowRuns` from `better-supabase/blocks/workflows/react`; the [workflows block](https://bettersupabase.com/blocks/workflows) must be installed (`npx better-supabase add workflows`). Its page on bettersupabase.com is [/recipes/workflow-runs](https://bettersupabase.com/recipes/workflow-runs).

Web, with shadcn components:

```bash
npx shadcn add badge empty skeleton table
```

```tsx title="apps/examples/nextjs/src/features/workflows/components/workflow-runs.tsx"
"use client";

import { useWorkflowRuns } from "better-supabase/blocks/workflows/react";
import { WorkflowIcon } from "lucide-react";
import { useExtracted, useFormatter } from "next-intl";

import { Badge } from "@/components/ui/badge";
import {
  Empty,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from "@/components/ui/empty";
import { Skeleton } from "@/components/ui/skeleton";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Link } from "@/i18n/navigation";

import { statusVariant, workflowName } from "../workflow-labels";

/**
 * The organization's runs from the workflows block, loaded again when a run
 * changes status (`workflow-runs:<tenant>`).
 */
export function WorkflowRuns({ tenant }: { tenant: string | null }) {
  const t = useExtracted("workflows");
  const format = useFormatter();

  const { runs, status } = useWorkflowRuns({
    tenant,
    schema: "api",
    limit: 50,
  });

  if (runs === undefined) {
    return <WorkflowRunsSkeleton />;
  }

  if (runs.length === 0) {
    return (
      <Empty className="border">
        <EmptyHeader>
          <EmptyMedia variant="icon">
            <WorkflowIcon />
          </EmptyMedia>
          <EmptyTitle>{t("No runs yet")}</EmptyTitle>
          <EmptyDescription>
            {t("Start a sample workflow to see its run here.")}
          </EmptyDescription>
        </EmptyHeader>
      </Empty>
    );
  }

  return (
    <div
      className="rounded-xl border"
      data-testid="workflow-runs"
      data-status={status}
    >
      <Table>
        <TableHeader>
          <TableRow>
            <TableHead>{t("Workflow")}</TableHead>
            <TableHead>{t("Status")}</TableHead>
            <TableHead className="text-right">{t("Started")}</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          {runs.map((run) => (
            <TableRow key={run.id}>
              <TableCell>
                <Link href={`/workflows/${run.id}`} className="font-medium">
                  {workflowName(run.definition)}
                </Link>
              </TableCell>
              <TableCell>
                <Badge variant={statusVariant(run.status)}>{run.status}</Badge>
              </TableCell>
              <TableCell className="text-right text-xs text-muted-foreground tabular-nums">
                {format.dateTime(new Date(run.createdAt.epochMilliseconds), {
                  dateStyle: "medium",
                  timeStyle: "short",
                })}
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  );
}

export function WorkflowRunsSkeleton() {
  return (
    <div className="space-y-2" aria-busy="true">
      {Array.from({ length: 4 }, (_, index) => (
        <Skeleton key={index} className="h-11 w-full" />
      ))}
    </div>
  );
}
```