QUESTPIE

Actions

CRUD actions and custom actions in the admin panel.

Actions are server-executed operations triggered from the admin UI. Every collection gets built-in CRUD actions automatically, and you can define custom actions with forms, confirmation dialogs, and server-side handlers.

How Actions Work

When a user opens a record or selects rows in a list view, the admin panel shows available actions in the toolbar. Each action:

  1. Checks access — the action is hidden if the user lacks permission
  2. Shows UI — optional confirmation dialog or custom form
  3. Executes server-side — runs the handler with full access to the app context (db, collections, queue, email, etc.)
  4. Refreshes data — the admin panel re-fetches after the action completes

Actions appear in two locations:

LocationScopeExample
Form view toolbarSingle recordSave, Delete, Duplicate, Publish
List view bulk toolbarMultiple recordsDelete selected, Export

Action Types

Built-in Actions

Every collection automatically gets Create, Save, Delete, and Duplicate actions. These respect your .access() rules — if a user can't delete, the Delete button is hidden.

See Built-in Actions for details.

Custom Actions

Define your own actions with custom forms and server handlers using the .actions() extension method:

.actions(({ a, c }) => ({
  custom: [
    a.action({
      id: "publish",
      label: { en: "Publish", sk: "Publikovat" },
      icon: c.icon("ph:paper-plane-tilt"),
      handler: async ({ itemId, collections }) => {
        await collections.posts.updateById({
          id: itemId!,
          data: { status: "published", publishedAt: new Date() },
        });
        return { type: "success", toast: { message: "Published" } };
      },
    }),
    a.action({
      id: "send-newsletter",
      label: "Send Newsletter",
      confirmation: {
        title: "Send newsletter?",
        description: "This will send to all subscribers.",
      },
      handler: async ({ itemId, collections, queue }) => {
        const post = await collections.posts.findOne({
          where: { id: itemId! },
        });
        if (!post) {
          return { type: "error", toast: { message: "Post not found" } };
        }
        await queue.sendNewsletter.publish({ postId: post.id });
        return { type: "success", toast: { message: "Newsletter queued" } };
      },
    }),
  ],
}))

See Custom Actions for the full guide.

Sections

On this page