0
# Actions & Button Components
1
2
Button variants, action lists, and interactive components for user actions and commands. These components provide consistent interaction patterns, accessibility features, and visual feedback for all user-initiated actions.
3
4
## Capabilities
5
6
### Button
7
8
Primary button component with multiple variants, sizes, tones, and interaction states for triggering actions.
9
10
```typescript { .api }
11
/**
12
* Primary button component for triggering actions
13
* @param children - Button content/label
14
* @param onClick - Button click handler
15
* @param variant - Button visual variant
16
* @param size - Button size
17
* @returns JSX element with button
18
*/
19
function Button(props: ButtonProps): JSX.Element;
20
21
interface ButtonProps extends BaseButton {
22
/** The content to display inside the button */
23
children?: string | string[];
24
/** Changes the size of the button, giving it more or less padding */
25
size?: 'micro' | 'slim' | 'medium' | 'large';
26
/** Changes the inner text alignment of the button */
27
textAlign?: 'left' | 'right' | 'center' | 'start' | 'end';
28
/** Allows the button to grow to the width of its container */
29
fullWidth?: boolean;
30
/** Displays the button with a disclosure icon. Defaults to down when set to true */
31
disclosure?: 'down' | 'up' | 'select' | boolean;
32
/** Icon to display to the left of the button content */
33
icon?: React.ReactElement | IconSource;
34
/** Indicates whether or not the button is the primary navigation link when rendered inside of an IndexTable.Row */
35
dataPrimaryLink?: boolean;
36
/** Sets the color treatment of the Button */
37
tone?: 'critical' | 'success';
38
/** Changes the visual appearance of the Button */
39
variant?: 'plain' | 'primary' | 'secondary' | 'tertiary' | 'monochromePlain';
40
/** Button ID */
41
id?: string;
42
/** Accessibility label */
43
accessibilityLabel?: string;
44
/** ARIA describedby */
45
ariaDescribedBy?: string;
46
/** ARIA expanded state */
47
ariaExpanded?: boolean;
48
/** ARIA controls */
49
ariaControls?: string;
50
/** Button role */
51
role?: string;
52
/** Tab index */
53
tabIndex?: number;
54
/** Submit button type */
55
submit?: boolean;
56
/** Button URL for link behavior */
57
url?: string;
58
/** External link */
59
external?: boolean;
60
/** Download attribute */
61
download?: string | boolean;
62
/** Link target */
63
target?: string;
64
/** Button pressed state */
65
pressed?: boolean;
66
/** Removes underline from button text (including on interaction) @deprecated Use a variant instead */
67
removeUnderline?: boolean;
68
/** Connector top */
69
connectedTop?: boolean;
70
/** Connector bottom */
71
connectedBottom?: boolean;
72
/** Connector left */
73
connectedLeft?: boolean;
74
/** Connector right */
75
connectedRight?: boolean;
76
/** Focus event handler */
77
onFocus?: () => void;
78
/** Blur event handler */
79
onBlur?: () => void;
80
/** Key down handler */
81
onKeyDown?: (event: React.KeyboardEvent) => void;
82
/** Key press handler */
83
onKeyPress?: (event: React.KeyboardEvent) => void;
84
/** Key up handler */
85
onKeyUp?: (event: React.KeyboardEvent) => void;
86
/** Mouse enter handler */
87
onMouseEnter?: () => void;
88
/** Touch start handler */
89
onTouchStart?: () => void;
90
}
91
```
92
93
**Usage Example:**
94
95
```typescript
96
import React, { useState } from 'react';
97
import { Button, ButtonGroup, InlineStack } from '@shopify/polaris';
98
99
function ButtonExamples() {
100
const [loading, setLoading] = useState(false);
101
102
const handleSave = async () => {
103
setLoading(true);
104
// Simulate API call
105
await new Promise(resolve => setTimeout(resolve, 2000));
106
setLoading(false);
107
};
108
109
return (
110
<InlineStack gap="400">
111
{/* Primary actions */}
112
<Button
113
variant="primary"
114
onClick={handleSave}
115
loading={loading}
116
>
117
Save product
118
</Button>
119
120
{/* Secondary actions */}
121
<Button variant="secondary" tone="critical">
122
Delete
123
</Button>
124
125
{/* Icon buttons */}
126
<Button
127
icon="search"
128
accessibilityLabel="Search products"
129
iconOnly
130
/>
131
132
{/* Button group */}
133
<ButtonGroup>
134
<Button>Cancel</Button>
135
<Button variant="primary">Save</Button>
136
</ButtonGroup>
137
</InlineStack>
138
);
139
}
140
```
141
142
### Button Utilities
143
144
Utility functions for creating buttons from action objects and converting action arrays to button arrays.
145
146
```typescript { .api }
147
/**
148
* Create Button component from Action object
149
* @param action - Action configuration
150
* @param overrides - Button prop overrides
151
* @returns JSX element with button from action
152
*/
153
function buttonFrom(
154
action: Action,
155
overrides?: Partial<ButtonProps>
156
): JSX.Element;
157
158
/**
159
* Create Button array from Action array
160
* @param actions - Array of action configurations
161
* @param overrides - Button prop overrides for all buttons
162
* @returns Array of JSX elements with buttons from actions
163
*/
164
function buttonsFrom(
165
actions: Action[],
166
overrides?: Partial<ButtonProps>
167
): JSX.Element[];
168
```
169
170
### UnstyledButton
171
172
Unstyled button base component providing button functionality without Polaris styling.
173
174
```typescript { .api }
175
/**
176
* Unstyled button base component
177
* @param children - Button content
178
* @param onClick - Click handler
179
* @param disabled - Disabled state
180
* @returns JSX element with unstyled button
181
*/
182
function UnstyledButton(props: UnstyledButtonProps): JSX.Element;
183
184
interface UnstyledButtonProps {
185
/** Button content */
186
children?: React.ReactNode;
187
/** Click handler */
188
onClick?: () => void;
189
/** Disabled state */
190
disabled?: boolean;
191
/** Loading state */
192
loading?: boolean;
193
/** Accessibility label */
194
accessibilityLabel?: string;
195
/** ARIA describedby */
196
ariaDescribedBy?: string;
197
/** ARIA expanded */
198
ariaExpanded?: boolean;
199
/** ARIA controls */
200
ariaControls?: string;
201
/** ARIA pressed */
202
ariaPressed?: boolean;
203
/** Button ID */
204
id?: string;
205
/** Tab index */
206
tabIndex?: number;
207
/** Button role */
208
role?: string;
209
/** Button type */
210
type?: 'button' | 'submit' | 'reset';
211
/** Button URL */
212
url?: string;
213
/** External link */
214
external?: boolean;
215
/** Download attribute */
216
download?: string | boolean;
217
/** Link target */
218
target?: string;
219
/** Additional class names */
220
className?: string;
221
/** Focus handler */
222
onFocus?: () => void;
223
/** Blur handler */
224
onBlur?: () => void;
225
/** Key down handler */
226
onKeyDown?: (event: React.KeyboardEvent) => void;
227
/** Key press handler */
228
onKeyPress?: (event: React.KeyboardEvent) => void;
229
/** Key up handler */
230
onKeyUp?: (event: React.KeyboardEvent) => void;
231
/** Mouse enter handler */
232
onMouseEnter?: () => void;
233
/** Touch start handler */
234
onTouchStart?: () => void;
235
}
236
237
/**
238
* Create UnstyledButton from Action object
239
* @param action - Action configuration
240
* @param overrides - Button prop overrides
241
* @returns JSX element with unstyled button from action
242
*/
243
function unstyledButtonFrom(
244
action: Action,
245
overrides?: Partial<UnstyledButtonProps>
246
): JSX.Element;
247
```
248
249
### ButtonGroup
250
251
Group multiple buttons with consistent spacing and connected styling options.
252
253
```typescript { .api }
254
/**
255
* Group buttons with consistent spacing and styling
256
* @param children - Button components
257
* @param segmented - Connected button styling
258
* @param fullWidth - Full width button group
259
* @returns JSX element with button group
260
*/
261
function ButtonGroup(props: ButtonGroupProps): JSX.Element;
262
263
interface ButtonGroupProps {
264
/** Button components */
265
children?: React.ReactNode;
266
/** Connected button styling */
267
segmented?: boolean;
268
/** Full width buttons */
269
fullWidth?: boolean;
270
/** Connector top */
271
connectedTop?: boolean;
272
/** Connector bottom */
273
connectedBottom?: boolean;
274
/** Button group variant */
275
variant?: 'segmented';
276
}
277
```
278
279
### ActionList
280
281
List of clickable actions with icons, descriptions, and keyboard navigation support.
282
283
```typescript { .api }
284
/**
285
* List of clickable actions with icons and descriptions
286
* @param items - Action items to display
287
* @param sections - Grouped action sections
288
* @param onActionAnyItem - Global action handler
289
* @returns JSX element with action list
290
*/
291
function ActionList(props: ActionListProps): JSX.Element;
292
293
interface ActionListProps {
294
/** Individual action items */
295
items?: ActionListItemDescriptor[];
296
/** Grouped action sections */
297
sections?: ActionListSection[];
298
/** Action role for accessibility */
299
actionRole?: string;
300
/** Global action handler */
301
onActionAnyItem?: ActionListItemDescriptor['onAction'];
302
}
303
304
interface ActionListItemDescriptor {
305
/** Action content/label */
306
content?: React.ReactNode;
307
/** Action callback */
308
onAction?(): void;
309
/** Action URL */
310
url?: string;
311
/** External link */
312
external?: boolean;
313
/** Action icon */
314
icon?: IconSource;
315
/** Action image */
316
image?: string;
317
/** Prefix content */
318
prefix?: React.ReactNode;
319
/** Suffix content */
320
suffix?: React.ReactNode;
321
/** Help text */
322
helpText?: React.ReactNode;
323
/** Accessibility label */
324
accessibilityLabel?: string;
325
/** Disabled state */
326
disabled?: boolean;
327
/** Destructive action */
328
destructive?: boolean;
329
/** Ellipsis truncation */
330
ellipsis?: boolean;
331
/** Active state */
332
active?: boolean;
333
/** Badge content */
334
badge?: BadgeProps;
335
/** Role override */
336
role?: string;
337
/** Truncate text */
338
truncate?: boolean;
339
/** Indentation variant */
340
variant?: 'default' | 'indented' | 'menu';
341
}
342
343
interface ActionListSection {
344
/** Section title */
345
title?: string;
346
/** Section items */
347
items: ActionListItemDescriptor[];
348
/** Fill available space */
349
fill?: boolean;
350
}
351
352
/**
353
* Individual action list item component
354
* @param content - Item content
355
* @param onAction - Action handler
356
* @param icon - Item icon
357
* @returns JSX element with action item
358
*/
359
function ActionListItem(props: ActionListItemProps): JSX.Element;
360
361
interface ActionListItemProps extends ActionListItemDescriptor {
362
/** Item index for keyboard navigation */
363
index?: number;
364
}
365
```
366
367
### ActionMenu
368
369
Menu component for displaying grouped actions with overflow handling and responsive behavior.
370
371
```typescript { .api }
372
/**
373
* Menu for displaying grouped actions with overflow handling
374
* @param groups - Action groups to display
375
* @param actions - Individual actions
376
* @param rollup - Overflow rollup configuration
377
* @returns JSX element with action menu
378
*/
379
function ActionMenu(props: ActionMenuProps): JSX.Element;
380
381
interface ActionMenuProps {
382
/** Action groups */
383
groups?: MenuGroupDescriptor[];
384
/** Individual actions */
385
actions?: MenuActionDescriptor[];
386
/** Overflow rollup behavior */
387
rollup?: boolean;
388
/** Rollup actions configuration */
389
rollupActionsLabel?: string;
390
/** Callback when menu opens */
391
onOpen?(): void;
392
/** Callback when menu closes */
393
onClose?(): void;
394
}
395
396
interface MenuGroupDescriptor {
397
/** Group title */
398
title?: string;
399
/** Group actions */
400
actions: MenuActionDescriptor[];
401
/** Group icon */
402
icon?: IconSource;
403
/** Details content */
404
details?: React.ReactNode;
405
/** Disabled state */
406
disabled?: boolean;
407
}
408
409
interface MenuActionDescriptor extends ActionListItemDescriptor {
410
/** Help text tooltip */
411
helpText?: string;
412
/** Action index for positioning */
413
index?: number;
414
}
415
```
416
417
### PageActions
418
419
Page-level action buttons positioned consistently in page headers with primary and secondary action support.
420
421
```typescript { .api }
422
/**
423
* Page-level action buttons for page headers
424
* @param primaryAction - Primary page action
425
* @param secondaryActions - Secondary page actions
426
* @returns JSX element with page actions
427
*/
428
function PageActions(props: PageActionsProps): JSX.Element;
429
430
interface PageActionsProps {
431
/** Primary page action */
432
primaryAction?: ComplexAction;
433
/** Secondary page actions */
434
secondaryActions?: ComplexAction[];
435
}
436
437
interface ComplexAction extends Action {
438
/** Accessibility label */
439
accessibilityLabel?: string;
440
/** Disabled state */
441
disabled?: boolean;
442
/** Destructive styling */
443
destructive?: boolean;
444
/** External link */
445
external?: boolean;
446
/** Help text */
447
helpText?: React.ReactNode;
448
/** Action icon */
449
icon?: IconSource;
450
/** Action ID */
451
id?: string;
452
/** Loading state */
453
loading?: boolean;
454
/** Outline variant */
455
outline?: boolean;
456
/** Plain variant */
457
plain?: boolean;
458
/** Primary styling */
459
primary?: boolean;
460
/** Remove underline */
461
removeUnderline?: boolean;
462
/** Size variant */
463
size?: 'slim' | 'medium' | 'large';
464
/** Submit button */
465
submit?: boolean;
466
/** Target for links */
467
target?: Target;
468
/** Tooltip content */
469
tooltip?: string;
470
}
471
```
472
473
**Usage Example:**
474
475
```typescript
476
import React from 'react';
477
import { ActionList, ActionMenu, Popover, Button } from '@shopify/polaris';
478
479
function ActionsExample() {
480
const [active, setActive] = React.useState(false);
481
482
const actionItems = [
483
{
484
content: 'Edit product',
485
icon: 'edit',
486
onAction: () => console.log('Edit clicked'),
487
},
488
{
489
content: 'Duplicate product',
490
icon: 'duplicate',
491
onAction: () => console.log('Duplicate clicked'),
492
},
493
{
494
content: 'Archive product',
495
icon: 'archive',
496
destructive: true,
497
onAction: () => console.log('Archive clicked'),
498
},
499
];
500
501
const menuGroups = [
502
{
503
title: 'Promote',
504
actions: [
505
{
506
content: 'Share on social media',
507
icon: 'share',
508
onAction: () => console.log('Share clicked'),
509
},
510
],
511
},
512
{
513
title: 'Organize',
514
actions: [
515
{
516
content: 'Add to collection',
517
icon: 'collection',
518
onAction: () => console.log('Add to collection'),
519
},
520
{
521
content: 'Add tags',
522
icon: 'tag',
523
onAction: () => console.log('Add tags'),
524
},
525
],
526
},
527
];
528
529
const activator = (
530
<Button
531
disclosure
532
onClick={() => setActive(!active)}
533
>
534
More actions
535
</Button>
536
);
537
538
return (
539
<>
540
{/* Simple action list */}
541
<ActionList items={actionItems} />
542
543
{/* Action menu with groups */}
544
<Popover
545
active={active}
546
activator={activator}
547
onClose={() => setActive(false)}
548
>
549
<ActionMenu groups={menuGroups} />
550
</Popover>
551
</>
552
);
553
}
554
```
555
556
## Action Types and Interfaces
557
558
```typescript { .api }
559
/** Base action interface */
560
interface Action {
561
/** Action content/label */
562
content?: string;
563
/** Accessibility label */
564
accessibilityLabel?: string;
565
/** Action URL */
566
url?: string;
567
/** External link */
568
external?: boolean;
569
/** Action callback */
570
onAction?(): void;
571
/** Mouse enter handler */
572
onMouseEnter?(): void;
573
/** Touch start handler */
574
onTouchStart?(): void;
575
}
576
577
/** Link action with required URL */
578
interface LinkAction extends Action {
579
/** Required URL for link actions */
580
url: string;
581
}
582
583
/** Callback action with required handler */
584
interface CallbackAction extends Action {
585
/** Required callback for action */
586
onAction(): void;
587
}
588
589
/** Disableable action interface */
590
interface DisableableAction extends Action {
591
/** Disabled state */
592
disabled?: boolean;
593
}
594
595
/** Destructable action interface */
596
interface DestructableAction extends Action {
597
/** Destructive styling */
598
destructive?: boolean;
599
}
600
601
/** Iconable action interface */
602
interface IconableAction extends Action {
603
/** Action icon */
604
icon?: IconSource;
605
}
606
607
/** Loadable action interface */
608
interface LoadableAction extends Action {
609
/** Loading state */
610
loading?: boolean;
611
}
612
613
/** Outlineable action interface */
614
interface OutlineableAction extends Action {
615
/** Outline variant */
616
outline?: boolean;
617
}
618
619
/** Plain action interface */
620
interface PlainAction extends Action {
621
/** Plain styling variant */
622
plain?: boolean;
623
}
624
625
/** Tooltip action interface */
626
interface TooltipAction extends Action {
627
/** Help text tooltip */
628
helpText?: React.ReactNode;
629
}
630
631
/** Badge action interface */
632
interface BadgeAction extends Action {
633
/** Badge configuration */
634
badge?: string | BadgeProps;
635
}
636
637
/** Icon source type */
638
type IconSource = React.ComponentType<any> | 'placeholder' | string;
639
640
/** Link target type */
641
type Target = '_blank' | '_self' | '_parent' | '_top';
642
643
/** Badge props interface */
644
interface BadgeProps {
645
/** Badge content */
646
children?: React.ReactNode;
647
/** Badge tone */
648
tone?: 'default' | 'success' | 'info' | 'attention' | 'warning' | 'critical';
649
/** Badge progress */
650
progress?: 'incomplete' | 'partiallyComplete' | 'complete';
651
/** Badge size */
652
size?: 'small' | 'medium';
653
}
654
```
655
656
### OptionList
657
658
Selectable list of options with keyboard navigation, ideal for dropdowns, filters, and selection interfaces.
659
660
```typescript { .api }
661
/**
662
* Selectable list with keyboard navigation
663
* @param options - Array of selectable options
664
* @param selected - Currently selected options
665
* @param onChange - Selection change handler
666
* @returns JSX element with option list
667
*/
668
function OptionList(props: OptionListProps): JSX.Element;
669
670
interface OptionListProps {
671
/** The title of the option list */
672
title?: string;
673
/** Collection of options to be rendered */
674
options?: SectionDescriptor[] | OptionDescriptor[];
675
/** The selected options */
676
selected: string[];
677
/** Allow more than one option to be selected */
678
allowMultiple?: boolean;
679
/** The role for the option list */
680
role?: string;
681
/** The option list's section role */
682
sectionRole?: string;
683
/** Callback when the selection of options is changed */
684
onChange(selected: string[]): void;
685
}
686
```
687
688
### SelectAllActions
689
690
Bulk selection controls for data tables and lists with select all, deselect all, and selective actions.
691
692
```typescript { .api }
693
/**
694
* Bulk selection controls for data lists
695
* @param label - Label for the select all control
696
* @param selected - Whether all items are selected
697
* @param onToggleAll - Toggle all selection handler
698
* @returns JSX element with select all controls
699
*/
700
function SelectAllActions(props: SelectAllActionsProps): JSX.Element;
701
702
interface SelectAllActionsProps {
703
/** Label for the bulk actions */
704
label?: string;
705
/** Callback when the selection of all items is changed */
706
onToggleAll?(): void;
707
/** Whether to select all items */
708
selectMode?: boolean;
709
/** Callback when the select mode is toggled */
710
onSelectModeToggle?(): void;
711
}
712
```
713
714
### SettingToggle
715
716
Toggle control for enabling/disabling settings with descriptive labels and contextual actions.
717
718
```typescript { .api }
719
/**
720
* Toggle control for settings with descriptive labels
721
* @param action - Toggle action configuration
722
* @param enabled - Whether setting is enabled
723
* @param children - Description content
724
* @returns JSX element with setting toggle
725
*/
726
function SettingToggle(props: SettingToggleProps): JSX.Element;
727
728
interface SettingToggleProps {
729
/** The action to toggle the setting */
730
action?: ComplexAction;
731
/** Whether the setting is enabled */
732
enabled?: boolean;
733
/** The content to display with the setting */
734
children?: React.ReactNode;
735
}
736
```
737
738
### Listbox
739
740
Accessible listbox component with support for single and multiple selection, keyboard navigation, and custom option rendering.
741
742
```typescript { .api }
743
/**
744
* Accessible listbox with selection and keyboard navigation
745
* @param options - Array of listbox options
746
* @param selected - Currently selected values
747
* @param onSelect - Selection change handler
748
* @returns JSX element with listbox interface
749
*/
750
function Listbox(props: ListboxProps): JSX.Element;
751
752
interface ListboxProps {
753
/** Enable auto selection behavior */
754
enableKeyboardControl?: boolean;
755
/** Collection of options to render */
756
children?: React.ReactNode;
757
/** Auto selection mode */
758
autoSelection?: AutoSelection;
759
/** Callback when an option is selected */
760
onSelect?(value: string): void;
761
/** Callback when the active option changes */
762
onActiveOptionChange?(value?: string): void;
763
}
764
765
enum AutoSelection {
766
None = 'NONE',
767
First = 'FIRST',
768
Selected = 'SELECTED',
769
}
770
```
771
772
### Tabs
773
774
Tab navigation component for organizing content into switchable panels with keyboard accessibility.
775
776
```typescript { .api }
777
/**
778
* Tab navigation for organizing content into panels
779
* @param tabs - Array of tab definitions
780
* @param selected - Index of selected tab
781
* @param onSelect - Tab selection handler
782
* @returns JSX element with tab navigation
783
*/
784
function Tabs(props: TabsProps): JSX.Element;
785
786
interface TabsProps {
787
/** Collection of tabs to display */
788
tabs: TabProps[];
789
/** The index of the selected tab */
790
selected: number;
791
/** Whether the tabs are fitted to the container */
792
fitted?: boolean;
793
/** The callback to handle tab selection */
794
onSelect?(selectedTabIndex: number): void;
795
/** Visually hide the tab navigation */
796
disclosureText?: string;
797
}
798
799
interface TabProps {
800
/** A unique identifier for the tab */
801
id: string;
802
/** Content to display in the tab */
803
content: React.ReactNode;
804
/** Accessible label for the tab */
805
accessibilityLabel?: string;
806
/** URL for the tab */
807
url?: string;
808
/** Badge content */
809
badge?: React.ReactNode;
810
/** Panel content */
811
panelID?: string;
812
}
813
```
814
815
### LegacyTabs
816
817
Legacy tab component for backward compatibility with older tab implementations.
818
819
```typescript { .api }
820
/**
821
* Legacy tab component for backward compatibility
822
* @param tabs - Array of legacy tab definitions
823
* @param selected - Index of selected tab
824
* @param onSelect - Tab selection handler
825
* @returns JSX element with legacy tab navigation
826
*/
827
function LegacyTabs(props: LegacyTabsProps): JSX.Element;
828
829
interface LegacyTabsProps {
830
/** Collection of tabs to display */
831
tabs: LegacyTab[];
832
/** The index of the selected tab */
833
selected: number;
834
/** Whether the tabs are fitted to the container */
835
fitted?: boolean;
836
/** The callback to handle tab selection */
837
onSelect?(selectedTabIndex: number): void;
838
}
839
840
interface LegacyTab {
841
/** A unique identifier for the tab */
842
id: string;
843
/** The title of the tab */
844
title: string;
845
/** URL for the tab */
846
url?: string;
847
/** Badge content */
848
badge?: React.ReactNode;
849
}
850
```