Admin extensions
Add your own screens, record cards and actions to the admin UI. Put a file in your app's admin/ folder; what it exports decides where it shows up. There's no manifest to write and nothing to register.
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,.jsxor.js, compiled like the rest of your app, and they import the 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 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.