---
title: "Repost UI"
description: "The portal's UI, open source: install the components into your codebase with the shadcn CLI, or build fully custom on the same hooks and client."
---

Everything the [hosted portal](/docs/send/portal) shows is built from open-source pieces you can use in your own product: delivery history, histograms, endpoint management, the dead-letter queue, the event catalog. Pick your level:

| Level | You get | You write |
|-------|---------|-----------|
| [Blocks](/docs/ui/blocks) | A working dashboard view per install, wired to live data. | A `getToken` callback. |
| [Components](/docs/ui/components) | Presentational pieces (tables, charts, forms) you feed with props. | The data wiring, via the [hooks](/docs/ui/portal-react). |
| [Libraries](/docs/ui/portal-react) | Typed hooks and a framework-agnostic client. | All of the UI. |

If you would rather not build screens at all, the portal itself embeds in your
app: whole, or one [bare surface](/docs/send/portal-surfaces) per page. See
[Embed the portal](/docs/send/portal-embed).

Installed code is yours: the [shadcn CLI](https://ui.shadcn.com/docs/cli) copies component source into your repo, where you restyle or rewrite it freely. Components never fetch data internally: data enters as props, so every piece works with any source of the same shape.

## Installation

The installation process is the same as [shadcn/ui](https://ui.shadcn.com/docs/installation).

<Steps>
  <Step title="Create project">
    Run the `init` command to create a new project or to set up an existing one:

    ```bash
    npx shadcn@latest init
    ```
  </Step>

  <Step title="Register the @repost namespace">
    Add the registry once to your `components.json`:

    ```json
    {
      "registries": {
        "@repost": "https://repost.sh/r/{name}.json"
      }
    }
    ```
  </Step>

  <Step title="Add components">
    ```bash
    npx shadcn@latest add @repost/webhook-dashboard
    ```

    The command copies the source into your project and resolves everything it needs: dependent components, npm packages, and the status color tokens (injected into your CSS with light and dark values).

    ```tsx
    import { WebhookDashboard } from "@/components/webhook-dashboard";

    export default function WebhooksPage() {
      return <WebhookDashboard getToken={getToken} />;
    }
    ```
  </Step>
</Steps>

## Auth model

Blocks and hooks authenticate with **portal access tokens**, the same short-lived, per-customer tokens that power hosted portal links. Your backend [mints them](/docs/send/portal) with your API key; the browser only ever sees the token. Every wired piece takes a `getToken` callback and handles refresh itself.

## Component gallery

[**repost.sh/docs/components**](/docs/components) is the component reference: one page per component with a live interactive preview, the demo source, props, and the installable source, plus a page per block.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Blocks" icon="layout-dashboard" href="/docs/ui/blocks" cta="Install" arrow="true">
    A working dashboard in one install.
  </Card>

  <Card title="Components" icon="component" href="/docs/ui/components" cta="Browse" arrow="true">
    Presentational pieces, props in.
  </Card>

  <Card title="React hooks" icon="plug" href="/docs/ui/portal-react" cta="Libraries" arrow="true">
    Build fully custom on the same data.
  </Card>
</Columns>
