FluiKitAction
FluiKitAction renders one action as a button or icon button.
It resolves permission, visibility, disabled state, confirmation, action execution, backend feedback status, and action events.
Props
type FluiKitActionProps = {
schema: NormalizedFluiKitConfig;
action: FluiKitActionConfig;
operation: string;
row?: Record<string, unknown>;
className?: string;
execute?: boolean;
display?: "button" | "icon";
onAction?: (resolution, result?) => unknown | Promise<unknown>;
onActionError?: (error, resolution) => void | Promise<void>;
};Minimal example
schemas/users/users.schema.ts
export const usersSchema = {
version: "1.0.0",
resource: "users",
identity: { key: "id", param: "id" },
actions: {
delete: {
enabled: true,
label: "Delete",
icon: "lucide:trash-2",
variant: "danger",
handler: "users.delete",
confirm: true
}
},
registry: {
actionHandlers: ["users.delete"]
}
} as const;lib/vibeflui/registry.ts
import { createRegistry } from "@vibeflui/core";
export const registry = createRegistry({
actionHandlers: {
"users.delete": async ({ identityValue }) => ({
status: true,
code: 200,
message: `User ${String(identityValue)} deleted`
})
}
});app/users/delete-action-preview.tsx
"use client";
import { FluiKitAction, FluiKitProvider, normalizeSchema } from "@vibeflui/core";
import { usersSchema } from "@/schemas/users/users.schema";
import { registry } from "@/lib/vibeflui/registry";
const schema = normalizeSchema(usersSchema);
const row = { id: "usr_1", name: "Ada Lovelace" };
function DeleteUserButton() {
return <FluiKitAction schema={schema} action={schema.actions.delete} operation="delete" row={row} />;
}
export function DeleteUserButtonPage() {
return (
<FluiKitProvider registry={registry}>
<DeleteUserButton />
</FluiKitProvider>
);
}This example needs FluiKitProvider because handler: "users.delete" resolves from registry.actionHandlers.
Default behavior
execute defaults to true.
The action renders nothing when:
schema.uiModeisreadonly— unless this is the view/detail action withaction.enabledexplicitlytrue; viewing isn't a mutation, so it's the one action that can opt back in under readonlyaction.enabledisfalse- permission resolution denies the action
action.visibleWhenevaluates to false for the current row
disabledWhen disables the rendered button without hiding it.
Display resolution
The display mode is resolved in this order:
displaypropaction.displayaction.iconOnly ? "icon" : "button"
The icon is resolved from action.icon, then from built-in defaults for create, edit/update, delete, view/detail, and bulk operations. Icon-only actions get mdi:dots-horizontal when no better default exists.
Confirmation
If action.confirm is truthy, the action opens FluiKitConfirmDialog before running.
confirm.secondConfirm can add a second confirmation step. This replaces the plain confirm: true on the delete action in the schema above:
actions: {
delete: {
enabled: true,
label: "Delete",
icon: "lucide:trash-2",
variant: "danger",
handler: "users.delete",
confirm: {
title: "Delete user?",
description: "This action cannot be undone.",
confirmLabel: "Delete",
secondConfirm: {
title: "Delete permanently?",
confirmLabel: "Yes, delete"
}
}
}
}Execution flow
When clicked, the component:
- emits
actionClicked - resolves the action with
resolveAction - executes
executeOperationwhenexecuteis true - checks backend feedback status from the operation result
- calls
onAction - checks backend feedback status from
onAction - emits
actionSucceededoractionFailed
If onActionError is not provided, thrown errors are rethrown.