Import
import { button, buttonGroup, primaryButton, dangerButton, outlineButton } from '@frontmcp/ui';
Basic Usage
// Primary button (default)
const html = button('Click Me');
// With variant
const secondary = button('Save', { variant: 'secondary' });
const danger = button('Delete', { variant: 'danger' });
Variants
Seven button variants for different purposes:
- primary - Main actions
- secondary - Secondary actions
- outline - Bordered, less prominent
- ghost - Text only, no background
- danger - Destructive actions
- success - Positive actions
- link - Looks like a link
button('Primary', { variant: 'primary' })
button('Secondary', { variant: 'secondary' })
button('Outline', { variant: 'outline' })
button('Ghost', { variant: 'ghost' })
button('Danger', { variant: 'danger' })
button('Success', { variant: 'success' })
button('Link', { variant: 'link' })
Shorthand Functions
import { primaryButton, secondaryButton, outlineButton, ghostButton, dangerButton, linkButton } from '@frontmcp/ui';
primaryButton('Submit')
dangerButton('Delete')
outlineButton('Cancel')
Sizes
Five size options: xs, sm, md (default), lg, xl.
button('Extra Small', { size: 'xs' })
button('Small', { size: 'sm' })
button('Medium', { size: 'md' })
button('Large', { size: 'lg' })
button('Extra Large', { size: 'xl' })
States
Loading State
const html = button('Saving...', {
loading: true,
disabled: true,
});
The loading state shows a spinner and visually indicates the button is busy.
Disabled State
const html = button('Unavailable', { disabled: true });
Full Width
const html = button('Full Width Button', { fullWidth: true });
Icons
Add icons before or after the button text:
// Icon before text
const html = button('Download', {
iconBefore: '<svg class="w-4 h-4" ...>...</svg>',
});
// Icon after text
const html = button('Next', {
iconAfter: '<svg class="w-4 h-4" ...>...</svg>',
});
// Icon only (accessibility: text becomes aria-label)
const html = button('Settings', {
iconBefore: '<svg class="w-5 h-5" ...>...</svg>',
iconOnly: true,
});
The iconBefore and iconAfter options accept raw HTML for SVG icons.
Only use trusted content (e.g., icon library output). Never pass user input.
Links
Render as an anchor tag with href:
const html = button('Visit Site', {
href: 'https://example.com',
target: '_blank', // Opens in new tab (adds rel="noopener noreferrer")
});
Only safe protocols are allowed: http://, https://, /, #, mailto:, tel:.
Dangerous protocols like javascript: are blocked.
HTMX Integration
Add dynamic behavior without JavaScript:
// Load content on click
const html = button('Load More', {
htmx: {
get: '/api/items?page=2',
target: '#items-list',
swap: 'beforeend',
},
});
// Submit form data
const submit = button('Submit', {
htmx: {
post: '/api/submit',
target: '#result',
swap: 'innerHTML',
},
});
// With confirmation
const deleteBtn = button('Delete', {
variant: 'danger',
htmx: {
delete: '/api/item/123',
target: '#item-123',
swap: 'outerHTML',
confirm: 'Are you sure you want to delete this?',
},
});
// With loading indicator
const loadBtn = button('Fetch Data', {
htmx: {
get: '/api/data',
target: '#result',
indicator: '#loading-spinner',
},
});
HTMX Options
| Option | Description |
|---|
get | HTTP GET request URL |
post | HTTP POST request URL |
put | HTTP PUT request URL |
delete | HTTP DELETE request URL |
target | CSS selector for response target |
swap | How to swap content |
trigger | Event that triggers request |
confirm | Confirmation dialog text |
indicator | Loading indicator selector |
Group related buttons together:
import { button, buttonGroup } from '@frontmcp/ui';
// Horizontal group (default)
const group = buttonGroup([
button('Edit', { variant: 'outline' }),
button('Duplicate', { variant: 'outline' }),
button('Delete', { variant: 'danger' }),
]);
// Attached buttons (no gap, rounded ends only)
const attached = buttonGroup([
button('Left'),
button('Center'),
button('Right'),
], { attached: true });
// Vertical group
const vertical = buttonGroup([
button('Option 1'),
button('Option 2'),
button('Option 3'),
], { direction: 'vertical' });
// Custom gap
const spaced = buttonGroup([
button('Save'),
button('Cancel', { variant: 'outline' }),
], { gap: 'lg' });
| Option | Type | Default | Description |
|---|
attached | boolean | false | Join buttons with no gap |
direction | 'horizontal' | 'vertical' | 'horizontal' | Layout direction |
gap | 'sm' | 'md' | 'lg' | 'md' | Space between buttons |
className | string | - | Additional CSS classes |
Data Attributes
Add custom data attributes:
const html = button('Track Click', {
data: {
'analytics-id': 'btn-submit',
'action': 'signup',
},
});
// Renders: data-analytics-id="btn-submit" data-action="signup"
API Reference
button(text, options?)
| Parameter | Type | Description |
|---|
text | string | Button label (used as aria-label for icon-only) |
options | ButtonOptions | Configuration options |
| Option | Type | Default | Description |
|---|
variant | 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger' | 'success' | 'link' | 'primary' | Visual style |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Button size |
type | 'button' | 'submit' | 'reset' | 'button' | HTML button type |
disabled | boolean | false | Disable the button |
loading | boolean | false | Show loading spinner |
fullWidth | boolean | false | Full container width |
iconBefore | string | - | HTML for icon before text |
iconAfter | string | - | HTML for icon after text |
iconOnly | boolean | false | Hide text, show icon only |
className | string | - | Additional CSS classes |
id | string | - | Button ID |
name | string | - | Form field name |
value | string | - | Form field value |
href | string | - | Link URL (renders as <a>) |
target | string | - | Link target |
htmx | object | - | HTMX attributes |
data | Record<string, string> | - | Data attributes |
ariaLabel | string | - | Accessibility label |
Returns
string - HTML string for the button element.