Button
An action component with built-in RBAC support for declarative access control. Wraps MUI Button with permission-based rendering and disabling. Perfect for primary actions, destructive operations, publishing workflows, and contextual toolbar actions.
Quick Start
Copy & Paste
import { Button } from '@dashforge/ui';
<Button onClick={handleClick}>Click Me</Button>Examples
Common Button patterns and variants
Basic Button
<Button onClick={handleClick}>
Click Me
</Button>Primary Action
<Button variant="contained" onClick={handleSubmit}>
Submit Form
</Button>Disabled State
<Button variant="contained" disabled> Cannot Click </Button>
With RBAC
<Button
variant="contained"
color="error"
access={{
resource: 'user',
action: 'delete',
onUnauthorized: 'hide'
}}
onClick={handleDelete}
>
Delete User
</Button>Button Variants
MUI Button variants for different UI contexts
Contained (Primary)
<Button variant="contained" onClick={handleSave}>
Save Changes
</Button>Outlined (Secondary)
<Button variant="outlined" onClick={handleCancel}>
Cancel
</Button>Text (Tertiary)
<Button variant="text" onClick={handleReset}>
Reset
</Button>Destructive Actions
<Button variant="contained" color="error" onClick={handleDelete}>
Delete User
</Button>Dashforge Capabilities
What makes Button powerful in the Dashforge ecosystem
Full MUI Button API
All MUI Button props are supported: variant, color, size, startIcon, endIcon, fullWidth, and more. Button is a thin wrapper that preserves the complete MUI ergonomics.
Built-in RBAC Support
Declarative access control via the access prop. No need for scattered useCan() checks in userland. Hide, disable, or fallback to disabled based on user permissions.
Action Authorization
Perfect for controlling primary actions (Save, Submit), destructive operations (Delete, Archive), publishing workflows (Publish, Approve), and contextual actions (Edit, Duplicate).
Explicit Readonly Fallback
Buttons are action components without readonly semantics. When onUnauthorized: "readonly" is used, the button explicitly falls back to disabled state for safe, predictable behavior.
OR Logic for Disabled State
Combines explicit disabled prop with RBAC access state. Button is disabled if either source requires it, allowing you to mix authorization with loading states and validation.
Zero Dependencies on Form System
Button does not depend on DashFormContext, react-hook-form, or @dashforge/forms. It only depends on @dashforge/rbac for access control, making it universally usable.
Access Control (RBAC)
Control button visibility and interaction based on user permissions. Buttons can be hidden or disabled when users lack the required access. Integrates seamlessly with the Dashforge RBAC system for declarative action authorization.
Hide when unauthorized
<Button
variant="contained"
access={{
resource: 'user',
action: 'delete',
onUnauthorized: 'hide',
}}
>
Delete User
</Button>Disable when cannot execute
<Button
variant="contained"
access={{
resource: 'article',
action: 'publish',
onUnauthorized: 'disable',
}}
>
Publish Article
</Button>Readonly fallback
<Button
variant="contained"
access={{
resource: 'invoice',
action: 'approve',
onUnauthorized: 'readonly',
}}
>
Approve Invoice
</Button>Combined with disabled state
<Button
variant="contained"
disabled={isPublishing}
access={{
resource: 'article',
action: 'publish',
onUnauthorized: 'disable',
}}
>
Publish
</Button>Note: Buttons do not support readonly semantics. When onUnauthorized: 'readonly' is used, it falls back to disabled for safety. Combine access with explicit disabled for loading states or business rules.
Action Integration Scenarios
Real-world usage in forms, toolbars, and workflows
Form Submit Actions
function ArticleForm() {
const { handleSubmit, formState } = useForm();
const onSubmit = async (data) => {
await saveArticle(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* Form fields... */}
<Stack direction="row" spacing={2}>
<Button
variant="contained"
type="submit"
disabled={formState.isSubmitting}
>
{formState.isSubmitting ? 'Saving...' : 'Save Draft'}
</Button>
<Button
variant="contained"
color="primary"
disabled={formState.isSubmitting}
access={{
resource: 'article',
action: 'publish',
onUnauthorized: 'disable'
}}
onClick={handlePublish}
>
Publish
</Button>
</Stack>
</form>
);
}Destructive Actions with Confirmation
function UserActions({ userId }) {
const confirm = useConfirm();
const handleDelete = async () => {
const result = await confirm({
title: 'Delete User',
message: 'This action cannot be undone.',
confirmText: 'Delete',
cancelText: 'Cancel'
});
if (result.confirmed) {
await deleteUser(userId);
}
};
return (
<Button
variant="contained"
color="error"
access={{
resource: 'user',
action: 'delete',
onUnauthorized: 'hide'
}}
onClick={handleDelete}
>
Delete User
</Button>
);
}Toolbar Actions
function ArticleToolbar({ article }) {
return (
<Stack direction="row" spacing={1}>
<Button
variant="outlined"
size="small"
access={{
resource: 'article',
action: 'edit',
onUnauthorized: 'hide'
}}
onClick={handleEdit}
>
Edit
</Button>
<Button
variant="outlined"
size="small"
access={{
resource: 'article',
action: 'duplicate',
onUnauthorized: 'hide'
}}
onClick={handleDuplicate}
>
Duplicate
</Button>
<Button
variant="outlined"
size="small"
color="error"
access={{
resource: 'article',
action: 'archive',
onUnauthorized: 'hide'
}}
onClick={handleArchive}
>
Archive
</Button>
</Stack>
);
}API Reference
Complete props documentation
RBAC and Disabled State: Button combines explicit disabled prop with RBAC access state using OR logic. The button is disabled if either the explicit prop is true OR RBAC requires it (onUnauthorized: 'disable' or 'readonly').
| Prop | Type | Default | Description |
|---|---|---|---|
| access | AccessRequirement | - | RBAC access control requirement. Controls button visibility and interaction based on user permissions. Supports hide, disable, and readonly (falls back to disabled) strategies. |
| disabled | boolean | false | If true, the button is disabled. Combines with RBAC access state via OR logic. |
| variant | 'text' | 'outlined' | 'contained' | 'text' | Button visual style |
| color | 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning' | 'primary' | Button color theme |
| size | 'small' | 'medium' | 'large' | 'medium' | Button size |
| onClick | (event: React.MouseEvent<HTMLButtonElement>) => void | - | Callback fired when the button is clicked |
| children | React.ReactNode | - | Button content (text, icons, etc.) |
| startIcon | React.ReactNode | - | Element placed before the children |
| endIcon | React.ReactNode | - | Element placed after the children |
| fullWidth | boolean | false | If true, the button takes up the full width of its container |
| type | 'button' | 'submit' | 'reset' | 'button' | HTML button type attribute |
| href | string | - | URL to link to when the button is clicked. Renders as anchor element when provided. |
Type Definitions
interface ButtonProps extends Omit<MuiButtonProps, 'disabled'> {
access?: AccessRequirement;
disabled?: boolean;
}
interface AccessRequirement {
resource: string;
action: string;
onUnauthorized: 'hide' | 'disable' | 'readonly';
}Note: All other MUI Button props are supported through prop forwarding (component, sx, disableRipple, etc.). See MUI Button API for complete prop reference.
Implementation Notes
Important details and best practices
Readonly Fallback Behavior
Buttons are action components without readonly semantics. When access.onUnauthorized is set to "readonly", the button falls back to disabled state. This is an explicit, safe choice: a disabled button clearly signals that the action cannot be performed, preventing unintended execution.
OR Logic for Disabled State
The effective disabled state uses OR logic: effectiveDisabled = disabled || accessState.disabled || accessState.readonly. This means the button is disabled if ANY source requires it (explicit prop, RBAC disable, or RBAC readonly fallback).
RBAC is Authorization, Not All UI Logic
RBAC controls permissions (can user perform this action?), not application state (is action currently in progress?). Combine the access prop with explicit disabled for loading states, validation failures, or business rules.
Not a Form Field
Button does not integrate with DashFormContext or react-hook-form. It is a pure action component. Use it for triggering actions, not for form field behavior. For form submission, use type="submit" with standard HTML form semantics.
Hide vs Disable
Use onUnauthorized: "hide" when the action should not be visible to unauthorized users (e.g., Delete User). Use onUnauthorized: "disable" when the action should be visible but not executable (e.g., Publish Article when user can view but not publish).
Full MUI Button API
All MUI Button props are supported and forwarded correctly: variant, color, size, startIcon, endIcon, fullWidth, href, component, type, and more. Button is a thin wrapper that preserves complete MUI ergonomics.