---
title: Admin extensions
description: Your app's own screens, cards and actions in the admin, from an admin/ folder.
section: Operations
order: 3
---

# Admin extensions

<p class="lead">Add your own screens, record cards and actions to the <a href="/docs/admin-ui">admin UI</a>. Put a file in your app's <code>admin/</code> folder; what it exports decides where it shows up. There's no manifest to write and nothing to register.</p>

```tsx title="admin/orders.tsx"
import { signal } from "sluurp/reactive";
import { Button } from "sluurp/kit/button.js";

export const title = "Orders";
export const icon = "card";               // one of the admin's icons
export const collections = ["customers"]; // where Block, Action and Bulk show; all when left out

// A screen of its own, under Extensions in the sidebar.
export default ({ api }) => {
  const open = signal(0);
  api.send("/collections/orders/records?perPage=1&filter=status='open'")
    .then((page) => open.set(page.totalItems));
  return <p>{() => `${open()} open orders`}</p>;
};

// A card at the end of a record.
export const Block = ({ api, record }) => <p>Customer since {record.created.slice(0, 4)}</p>;

// An item in the record's More actions menu, opening a dialog.
export const Action = ({ record, toast, close }) => (
  <Button onClick={() => { toast("Sent", `Invoice to ${record.email}`); close(); }}>Send invoice</Button>
);

// A button on the selection bar when rows are ticked.
export const Bulk = ({ ids, toast, close }) => {
  toast("Tagged", `${ids.length} customers`);
  close();
  return "";
};
```

## What goes where

| Export | Shows up as |
|---|---|
| `default` | a screen, listed under **Extensions** in the sidebar and kept on reload |
| `Block` | a card at the end of a record, titled with `title` |
| `Action` | an item in a record's **More actions** menu, opening a dialog |
| `Bulk` | a button on the selection bar, opening a dialog |
| `title` | the name everywhere (defaults to the file name: `report-cards` → "Report cards") |
| `icon` | the icon in the sidebar and menus |
| `collections` | which collections get `Block`, `Action` and `Bulk` |

A file can export any combination of these.

## What each one receives

Every export gets:

- `api`, the admin's client, signed in as the superuser: `api.send(path, { method, body })`;
- `toast(title, body)`;
- `open(collection, id?)` to go to a collection or one of its records;
- `refresh()` to reload the open collection's records after changing them.

`Block` and `Action` also get `collection` and `record`. `Bulk` gets `collection` and the ticked `ids`. `Action` and `Bulk` get `close()`, which shuts their dialog; after a bulk action the table is read again.

## Good to know

- Files are `.tsx`, `.ts`, `.jsx` or `.js`, compiled like the rest of your app, and they import the [UI kit](/docs/ui-kit) the way your pages do.
- They run in the admin with no sandbox: extensions are your own code, trusted like a hook. They do run in the browser, though, so keep secrets out of them. Call a [server function](/docs/server-functions) for anything secret.
- If an extension fails to load or throws, it shows the error in its own place and the rest of the admin keeps working.
