| name | pimcore-studio-ui-buttons |
| description | Using button components in Pimcore Studio UI - Button, IconButton, IconTextButton, DropdownButton, and ButtonGroup |
| metadata | {"audience":"pimcore-developers","focus":"ui-components-buttons"} |
What This Skill Covers
How to use button components in Pimcore Studio UI:
- Button - Standard text buttons (primary, default, link, text types)
- IconButton - Icon-only buttons with tooltips (always include tooltips!)
- IconTextButton - Buttons combining icon + text
- DropdownButton - Buttons with dropdown chevron
- ButtonGroup - Grouping multiple related buttons
- Button loading states and disabled states
- Color variants (primary, secondary, danger)
- When to use each button type
When to Use This Skill
Use this when:
- Adding action buttons to toolbars, forms, or modals
- Creating icon-only toolbar buttons (always with tooltips)
- Building button groups for related actions
- Implementing save, cancel, delete, or action buttons
- Creating dropdown menus with button triggers
🚨 Import Paths
BEFORE writing any import, read CRITICAL-IMPORT-PATHS.md.
All examples below use bundle imports (@pimcore/studio-ui-bundle/*). For core development (@sdk/*, @Pimcore/*), see the referenced file.
Writing Style for Button Labels
Follow these capitalization and wording guidelines for all button text:
Sentence Case for Actions
Buttons and interactive elements use Sentence case - only the first word is capitalized:
<Button>{t('save')}</Button> // "Save"
<Button>{t('save-draft')}</Button> // "Save draft"
<Button>{t('merge-version')}</Button> // "Merge version"
<Button>{t('review-changes')}</Button> // "Review changes"
<Button>{t('export-csv')}</Button> // "Export CSV"
<Button>{t('confirm-delete')}</Button> // "Confirm delete"
<Button>{t('save-draft')}</Button> // Not "Save Draft"
<Button>{t('review-changes')}</Button> // Not "Review Changes"
This applies to:
- Buttons (all types)
- Modal action buttons
- Toolbar action buttons
- Form submit buttons
- Confirmation buttons
Action-Oriented Wording
Use verb-first phrasing for all actionable interface elements:
<Button>{t('export-csv')}</Button> // "Export CSV"
<Button>{t('download-file')}</Button> // "Download file"
<Button>{t('create-new')}</Button> // "Create new"
<Button>{t('save-changes')}</Button> // "Save changes"
<IconButton
tooltip={{ title: t('refresh-data') }}
/>
<Button>{t('csv-export')}</Button>
<Button>{t('file-download')}</Button>
<Button>{t('new-item')}</Button>
Translation Key Naming
Match your translation keys to the final output (sentence case, verb-first):
toolbar.save: Save
toolbar.save-draft: Save draft
toolbar.export-csv: Export CSV
toolbar.create-new: Create new
modal.confirm-delete: Confirm delete
modal.review-changes: Review changes
Writing Style Quick Reference
| Element | Case | Example | Translation Key |
|---|
| Buttons | Sentence case | "Save draft" | save-draft |
| Toolbar actions | Sentence case | "Export CSV" | export-csv |
| Modal buttons | Sentence case | "Confirm delete" | confirm-delete |
| Icon tooltips | Sentence case | "Refresh data" | refresh-data |
Remember:
- Buttons/actions = Sentence case + verb-first
- Always translate, never hardcode
For navigation labels (Title Case), see the pimcore-studio-ui-navigation skill.
Button Component
Standard button with text, the most common button type.
Button Types
type?: 'primary' | 'default' | 'link' | 'text' | 'action'
- primary - Main action button (blue, filled)
- default - Secondary action (gray border)
- link - Text-like button with no border
- text - Plain text button with hover state
- action - Special action style
Color Variants
color?: 'default' | 'primary' | 'secondary' | 'danger'
- primary - Blue emphasis
- secondary - Gray secondary actions
- danger - Red destructive actions (delete, remove)
Basic Usage
import { Button } from '@pimcore/studio-ui-bundle/components'
import { useTranslation } from 'react-i18next'
export const MyComponent = (): React.JSX.Element => {
const { t } = useTranslation()
return (
<Button
onClick={ handleSave }
type="primary"
>
{t('toolbar.save')}
</Button>
)
}
Button Props
interface ButtonProps {
type?: 'primary' | 'default' | 'link' | 'text' | 'action'
color?: 'default' | 'primary' | 'secondary' | 'danger'
loading?: boolean
disabled?: boolean
onClick?: () => void
children: React.ReactNode
className?: string
htmlType?: 'button' | 'submit' | 'reset'
}
Loading State
Buttons support loading states with a spinner. The button width is locked during loading to prevent layout shift:
const [updateAsset, { isLoading }] = useAssetUpdateByIdMutation()
return (
<Button
disabled={ isLoading }
loading={ isLoading }
onClick={ handleSave }
type="primary"
>
{t('toolbar.save-and-publish')}
</Button>
)
Real-World Example: Save Button
import { Button } from '@pimcore/studio-ui-bundle/components'
import { useTranslation } from 'react-i18next'
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
export const EditorToolbarSaveButton = (): React.JSX.Element => {
const { t } = useTranslation()
const { asset } = useAssetDraft(id)
const [saveAsset, { isLoading }] = useAssetUpdateByIdMutation()
return (
<>
{checkElementPermission(asset?.permissions, 'publish') && (
<Button
disabled={ isLoading }
loading={ isLoading }
onClick={ onSaveClick }
type="primary"
>
{t(asset?.type === 'folder' ? 'toolbar.save' : 'toolbar.save-and-publish')}
</Button>
)}
</>
)
}
IconButton Component
Icon-only buttons for toolbars and compact UIs.
🚨 CRITICAL: Always Include Tooltips!
IconButtons MUST always have tooltips for accessibility!
<IconButton
icon={ { value: 'delete' } }
onClick={ handleDelete }
/>
<IconButton
icon={ { value: 'delete' } }
onClick={ handleDelete }
tooltip={ { title: t('delete') } }
/>
IconButton Props
interface IconButtonProps {
icon: IconProps
tooltip?: TooltipProps
theme?: 'primary' | 'secondary'
variant?: 'minimal' | 'static'
size?: 'small' | 'middle' | 'large'
hideShadow?: boolean
type?: ButtonProps['type']
onClick?: () => void
disabled?: boolean
loading?: boolean
}
Icon Configuration
icon={ {
value: 'icon-name',
options: {
width: 16,
height: 16
}
} }
Common icons: 'refresh', 'delete', 'edit', 'plus', 'close', 'save', 'download', 'upload', 'chevron-down', 'chevron-right'
Theme Variants
<IconButton
icon={ { value: 'edit' } }
theme="primary"
tooltip={ { title: t('edit') } }
/>
<IconButton
icon={ { value: 'close' } }
theme="secondary"
tooltip={ { title: t('close') } }
/>
Real-World Example: Toolbar Refresh Button
import { IconButton } from '@pimcore/studio-ui-bundle/components'
import { useTranslation } from 'react-i18next'
export const TreeContainer = (): React.JSX.Element => {
const { t } = useTranslation()
const { refetch, isLoading } = useQuery()
return (
<Toolbar>
<IconButton
icon={ { value: 'refresh' } }
loading={ isLoading }
onClick={ async () => await refetch() }
tooltip={ { title: t('refresh') } }
/>
</Toolbar>
)
}
Size Variants
<IconButton
icon={ { value: 'delete' } }
size="small"
tooltip={ { title: t('delete') } }
/>
<IconButton
icon={ { value: 'delete' } }
tooltip={ { title: t('delete') } }
/>
<IconButton
icon={ {
value: 'delete',
options: { width: 24, height: 24 }
} }
tooltip={ { title: t('delete') } }
/>
IconTextButton Component
Buttons that combine an icon with text. Perfect for toolbar actions that need both visual and textual clarity.
IconTextButton Props
interface IconTextButtonProps extends Omit<ButtonProps, 'icon'> {
icon: IconProps
iconPlacement?: 'left' | 'right'
children: React.ReactNode
}
Basic Usage
import { IconTextButton } from '@pimcore/studio-ui-bundle/components'
import { useTranslation } from 'react-i18next'
export const MyToolbar = (): React.JSX.Element => {
const { t } = useTranslation()
return (
<IconTextButton
icon={ { value: 'plus' } }
onClick={ handleCreate }
type="primary"
>
{t('toolbar.create')}
</IconTextButton>
)
}
Icon Placement
<IconTextButton
icon={ { value: 'save' } }
>
{t('save')}
</IconTextButton>
<IconTextButton
icon={ { value: 'chevron-down' } }
iconPlacement="right"
>
{t('more-options')}
</IconTextButton>
Real-World Example: Create New Button
<Toolbar justify="space-between">
<IconButton
icon={ { value: 'refresh' } }
onClick={ refetch }
tooltip={ { title: t('refresh') } }
/>
<IconTextButton
icon={ { value: 'new' } }
loading={ isLoading }
onClick={ createWidget }
>
{t('toolbar.new')}
</IconTextButton>
</Toolbar>
Button Types with Icons
<IconTextButton
icon={ { value: 'save' } }
type="primary"
>
{t('save')}
</IconTextButton>
<IconTextButton
icon={ { value: 'download' } }
type="link"
>
{t('download')}
</IconTextButton>
<IconTextButton
color="danger"
icon={ { value: 'delete' } }
type="primary"
>
{t('delete')}
</IconTextButton>
DropdownButton Component
Button with a dropdown chevron icon on the right. Commonly used for dropdown menus and "More" actions.
DropdownButton Props
interface DropdownButtonProps extends Omit<IconTextButtonProps, 'icon'> {
icon?: IconProps
children: React.ReactNode
}
Basic Usage
import { DropdownButton } from '@pimcore/studio-ui-bundle/components'
import { Dropdown } from 'antd'
export const ActionsDropdown = (): React.JSX.Element => {
const { t } = useTranslation()
const items = [
{ key: 'edit', label: t('edit') },
{ key: 'delete', label: t('delete'), danger: true }
]
return (
<Dropdown menu={ { items } }>
<DropdownButton>
{t('more-actions')}
</DropdownButton>
</Dropdown>
)
}
With Custom Icon
<DropdownButton
icon={ { value: 'settings' } }
>
{t('settings')}
</DropdownButton>
Real-World Pattern: Batch Actions
import { DropdownButton } from '@pimcore/studio-ui-bundle/components'
import { Dropdown } from 'antd'
import type { MenuProps } from 'antd'
export const BatchActionsDropdown = (): React.JSX.Element => {
const { t } = useTranslation()
const selectedItems = useSelectedItems()
const menuItems: MenuProps['items'] = [
{
key: 'export',
label: t('batch.export'),
icon: <Icon value="download" />
},
{
key: 'delete',
label: t('batch.delete'),
icon: <Icon value="delete" />,
danger: true
}
]
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
switch (key) {
case 'export':
handleExport(selectedItems)
break
case 'delete':
handleDelete(selectedItems)
break
}
}
return (
<Dropdown
disabled={ selectedItems.length === 0 }
menu={ { items: menuItems, onClick: handleMenuClick } }
>
<DropdownButton disabled={ selectedItems.length === 0 }>
{t('batch-actions')} ({selectedItems.length})
</DropdownButton>
</Dropdown>
)
}
ButtonGroup Component
Groups multiple related buttons together with consistent spacing.
ButtonGroup Props
interface ButtonGroupProps {
items: ReactElement[]
withSeparator?: boolean
noSpacing?: boolean
}
Basic Usage
import { ButtonGroup, Button, IconButton } from '@pimcore/studio-ui-bundle/components'
export const EditorToolbar = (): React.JSX.Element => {
const { t } = useTranslation()
return (
<ButtonGroup
items={ [
<Button
key="save"
onClick={ handleSave }
type="primary"
>
{t('save')}
</Button>,
<Button
key="cancel"
onClick={ handleCancel }
>
{t('cancel')}
</Button>
] }
/>
)
}
With Separators
<ButtonGroup
items={ [
<IconButton
icon={ { value: 'undo' } }
key="undo"
onClick={ handleUndo }
tooltip={ { title: t('undo') } }
/>,
<IconButton
icon={ { value: 'redo' } }
key="redo"
onClick={ handleRedo }
tooltip={ { title: t('redo') } }
/>
] }
withSeparator
/>
Compact Style (No Spacing)
<ButtonGroup
items={ [
<IconButton
icon={ { value: 'align-left' } }
key="left"
tooltip={ { title: t('align-left') } }
/>,
<IconButton
icon={ { value: 'align-center' } }
key="center"
tooltip={ { title: t('align-center') } }
/>,
<IconButton
icon={ { value: 'align-right' } }
key="right"
tooltip={ { title: t('align-right') } }
/>
] }
noSpacing
/>
Real-World Pattern: Action Groups
import { ButtonGroup, Button, IconButton } from '@pimcore/studio-ui-bundle/components'
export const FormActions = (): React.JSX.Element => {
const { t } = useTranslation()
const mainActions = (
<ButtonGroup
items={ [
<Button
key="save"
loading={ isSaving }
onClick={ handleSave }
type="primary"
>
{t('save')}
</Button>,
<Button
key="cancel"
onClick={ handleCancel }
>
{t('cancel')}
</Button>
] }
/>
)
const secondaryActions = (
<ButtonGroup
items={ [
<IconButton
icon={ { value: 'delete' } }
key="delete"
onClick={ handleDelete }
tooltip={ { title: t('delete') } }
/>,
<IconButton
icon={ { value: 'duplicate' } }
key="duplicate"
onClick={ handleDuplicate }
tooltip={ { title: t('duplicate') } }
/>
] }
withSeparator
/>
)
return (
<Toolbar justify="space-between">
{mainActions}
{secondaryActions}
</Toolbar>
)
}
Common Button Patterns
Pattern 1: Save/Cancel Button Pair
<ButtonGroup
items={ [
<Button
disabled={ isLoading }
key="save"
loading={ isLoading }
onClick={ handleSave }
type="primary"
>
{t('save')}
</Button>,
<Button
disabled={ isLoading }
key="cancel"
onClick={ handleCancel }
>
{t('cancel')}
</Button>
] }
/>
Pattern 2: Toolbar Icon Buttons
<Toolbar>
<IconButton
icon={ { value: 'refresh' } }
loading={ isLoading }
onClick={ refetch }
tooltip={ { title: t('refresh') } }
/>
<IconButton
icon={ { value: 'settings' } }
onClick={ openSettings }
tooltip={ { title: t('settings') } }
/>
<IconTextButton
icon={ { value: 'plus' } }
onClick={ handleCreate }
type="primary"
>
{t('create-new')}
</IconTextButton>
</Toolbar>
Pattern 3: Confirmation Actions
<Button
color="danger"
onClick={ showDeleteConfirmation }
type="primary"
>
{t('delete')}
</Button>
<ButtonGroup
items={ [
<Button
key="confirm"
loading={ isDeleting }
onClick={ handleConfirmDelete }
type="primary"
color="danger"
>
{t('confirm-delete')}
</Button>,
<Button
disabled={ isDeleting }
key="cancel"
onClick={ closeModal }
>
{t('cancel')}
</Button>
] }
/>
Pattern 4: Permission-Based Buttons
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
{checkElementPermission(element?.permissions, 'publish') && (
<Button
onClick={ handlePublish }
type="primary"
>
{t('publish')}
</Button>
)}
<Button
disabled={ !checkElementPermission(element?.permissions, 'delete') }
onClick={ handleDelete }
color="danger"
>
{t('delete')}
</Button>
Pattern 5: Loading States with Multiple Operations
const [saveAsset, { isLoading: isSaving }] = useAssetUpdateByIdMutation()
const { saveSchedules, isLoading: isSavingSchedules } = useSaveSchedules()
const isLoading = isSaving || isSavingSchedules
return (
<Button
disabled={ isLoading }
loading={ isLoading }
onClick={ handleSave }
type="primary"
>
{t('save')}
</Button>
)
When to Use Each Button Type
Use <Button> when:
- Primary or secondary actions need text labels
- Form submit/cancel buttons
- Modal action buttons
- Clear text description is important
Use <IconButton> when:
- Space is limited (toolbars, table rows)
- Action is universally recognized by icon
- Multiple similar actions in a row
- ALWAYS with a tooltip!
Use <IconTextButton> when:
- Action benefits from both icon and text
- Primary toolbar actions
- Creating/adding new items
- Icon reinforces the text meaning
Use <DropdownButton> when:
- Multiple related actions in a menu
- "More actions" overflow menus
- Batch operations
- Settings/options menus
Use <ButtonGroup> when:
- Multiple related buttons should be visually grouped
- Save/Cancel pairs
- Toolbar button sets
- Action groups with clear relationships
Common Mistakes
❌ IconButton Without Tooltip
<IconButton
icon={ { value: 'delete' } }
onClick={ handleDelete }
/>
<IconButton
icon={ { value: 'delete' } }
onClick={ handleDelete }
tooltip={ { title: t('delete') } }
/>
❌ Wrong Button Type for Use Case
<Button
onClick={ handleCancel }
type="primary"
>
{t('cancel')}
</Button>
<Button
onClick={ handleCancel }
>
{t('cancel')}
</Button>
❌ Not Handling Loading States
<Button onClick={ handleSave }>
{t('save')}
</Button>
<Button
disabled={ isLoading }
loading={ isLoading }
onClick={ handleSave }
>
{t('save')}
</Button>
❌ Forgetting Translation Keys
<Button onClick={ handleSave }>
Save
</Button>
<Button onClick={ handleSave }>
{t('save')}
</Button>
❌ Missing Keys in ButtonGroup
<ButtonGroup
items={ [
<Button onClick={ handleSave }>Save</Button>,
<Button onClick={ handleCancel }>Cancel</Button>
] }
/>
<ButtonGroup
items={ [
<Button key="save" onClick={ handleSave }>{t('save')}</Button>,
<Button key="cancel" onClick={ handleCancel }>{t('cancel')}</Button>
] }
/>
Quick Reference
Button Component Comparison
| Component | Use Case | Always Include |
|---|
Button | Text actions, forms, modals | Translation |
IconButton | Compact toolbar actions | Tooltip + Translation |
IconTextButton | Primary toolbar actions | Icon + Translation |
DropdownButton | Menu triggers | Translation |
ButtonGroup | Related action groups | Keys for items |
Common Button Types
| Type | Appearance | Use For |
|---|
primary | Blue filled | Main/primary actions |
default | Gray border | Secondary actions |
link | Text with hover | Tertiary/inline actions |
text | Plain text | Minimal emphasis actions |
Common Button Colors
| Color | Use For |
|---|
primary | Positive/main actions |
secondary | Alternative actions |
danger | Destructive actions (delete, remove) |
Next Steps