0
# Feedback & Overlay Components
1
2
Modal dialogs, notifications, tooltips, and overlay components for providing user feedback, contextual information, and interactive experiences. These components handle focus management, accessibility, and proper layering.
3
4
## Capabilities
5
6
### Modal
7
8
Modal dialog component for displaying content in an overlay with backdrop, focus trapping, and accessibility features.
9
10
```typescript { .api }
11
/**
12
* Modal dialog overlay with backdrop and focus management
13
* @param open - Modal visibility state
14
* @param onClose - Modal close handler
15
* @param title - Modal title text
16
* @param children - Modal content
17
* @returns JSX element with modal dialog
18
*/
19
function Modal(props: ModalProps): JSX.Element;
20
21
interface ModalProps extends FooterProps {
22
/** Whether the modal is open or not */
23
open: boolean;
24
/** The url that will be loaded as the content of the modal */
25
src?: string;
26
/** The name of the modal content iframe */
27
iFrameName?: string;
28
/** The content for the title of the modal */
29
title: string | React.ReactNode;
30
/** Hide the title in the modal */
31
titleHidden?: boolean;
32
/** The content to display inside modal */
33
children?: React.ReactNode;
34
/** Inner content of the footer */
35
footer?: React.ReactNode;
36
/** Disable animations and open modal instantly */
37
instant?: boolean;
38
/** Automatically adds sections to modal */
39
sectioned?: boolean;
40
/** The size of the modal */
41
size?: ModalSize;
42
/** Limits modal height on large screens with scrolling */
43
limitHeight?: boolean;
44
/** Replaces modal content with a spinner while a background action is being performed */
45
loading?: boolean;
46
/** Callback when the modal is closed */
47
onClose(): void;
48
/** Callback when iframe has loaded */
49
onIFrameLoad?(evt: React.SyntheticEvent<HTMLIFrameElement>): void;
50
/** Callback when modal transition animation has ended */
51
onTransitionEnd?(): void;
52
/** Callback when the bottom of the modal content is reached */
53
onScrolledToBottom?(): void;
54
/** The element or the RefObject that activates the Modal */
55
activator?: React.RefObject<HTMLElement> | React.ReactElement;
56
/** The element type to wrap the activator in */
57
activatorWrapper?: Element;
58
/** Removes Scrollable container from the modal content */
59
noScroll?: boolean;
60
}
61
62
type ModalSize = 'small' | 'large' | 'fullScreen';
63
64
interface FooterProps {
65
primaryAction?: PrimaryAction;
66
secondaryActions?: SecondaryAction[];
67
}
68
69
interface PrimaryAction {
70
/** Action content */
71
content: string;
72
/** Action callback */
73
onAction: () => void;
74
/** Loading state */
75
loading?: boolean;
76
/** Disabled state */
77
disabled?: boolean;
78
/** Destructive styling */
79
destructive?: boolean;
80
}
81
82
interface SecondaryAction {
83
/** Action content */
84
content: string;
85
/** Action callback */
86
onAction: () => void;
87
/** Loading state */
88
loading?: boolean;
89
/** Disabled state */
90
disabled?: boolean;
91
/** Destructive styling */
92
destructive?: boolean;
93
/** Help text */
94
helpText?: string;
95
/** Accessibility label */
96
accessibilityLabel?: string;
97
/** Icon */
98
icon?: IconSource;
99
}
100
```
101
102
**Usage Example:**
103
104
```typescript
105
import React, { useState } from 'react';
106
import { Modal, Button, TextContainer, Text } from '@shopify/polaris';
107
108
function DeleteProductModal() {
109
const [active, setActive] = useState(false);
110
111
const handleClose = () => setActive(false);
112
const handleOpen = () => setActive(true);
113
114
const primaryAction = {
115
content: 'Delete product',
116
destructive: true,
117
onAction: () => {
118
// Handle delete
119
handleClose();
120
},
121
};
122
123
const secondaryActions = [
124
{
125
content: 'Cancel',
126
onAction: handleClose,
127
},
128
];
129
130
return (
131
<>
132
<Button destructive onClick={handleOpen}>
133
Delete Product
134
</Button>
135
<Modal
136
open={active}
137
onClose={handleClose}
138
title="Delete Product"
139
primaryAction={primaryAction}
140
secondaryActions={secondaryActions}
141
>
142
<Modal.Section>
143
<TextContainer>
144
<Text variant="bodyMd">
145
Are you sure you want to delete this product? This action cannot be undone.
146
</Text>
147
</TextContainer>
148
</Modal.Section>
149
</Modal>
150
</>
151
);
152
}
153
```
154
155
### Banner
156
157
Important messaging banner component for displaying alerts, notifications, and status information with various tones.
158
159
```typescript { .api }
160
/**
161
* Important messaging banner with tone and actions
162
* @param title - Banner title
163
* @param children - Banner content
164
* @param tone - Banner tone/color theme
165
* @param onDismiss - Dismiss handler
166
* @returns JSX element with banner
167
*/
168
function Banner(props: BannerProps): JSX.Element;
169
170
interface BannerProps {
171
/** Banner title */
172
title?: string;
173
/** Banner content */
174
children?: React.ReactNode;
175
/** Banner tone */
176
tone?: BannerTone;
177
/** Primary action */
178
action?: Action;
179
/** Secondary action */
180
secondaryAction?: Action;
181
/** Dismiss handler */
182
onDismiss?: () => void;
183
/** Hide icon */
184
hideIcon?: boolean;
185
/** Icon source override */
186
icon?: IconSource;
187
/** Stop announcements */
188
stopAnnouncements?: boolean;
189
}
190
191
/** Banner tone options */
192
enum BannerTone {
193
Info = 'info',
194
Success = 'success',
195
Warning = 'warning',
196
Critical = 'critical',
197
AttentionGrabbing = 'attention-grabbing',
198
}
199
200
interface BannerHandles {
201
/** Focus the banner */
202
focus(): void;
203
}
204
```
205
206
### Toast
207
208
Temporary notification component for brief messages with optional actions and automatic dismissal.
209
210
```typescript { .api }
211
/**
212
* Temporary notification with automatic dismissal
213
* @param content - Toast message content
214
* @param action - Optional action button
215
* @param duration - Display duration in milliseconds
216
* @param onDismiss - Dismiss handler
217
* @returns JSX element with toast notification
218
*/
219
function Toast(props: ToastProps): JSX.Element;
220
221
interface ToastProps {
222
/** Toast message content */
223
content: string;
224
/** Error state */
225
error?: boolean;
226
/** Toast action */
227
action?: Action;
228
/** Display duration */
229
duration?: number;
230
/** Dismiss handler */
231
onDismiss(): void;
232
/** Toast ID */
233
id?: string;
234
}
235
```
236
237
### Popover
238
239
Floating content overlay positioned relative to a trigger element with automatic positioning and interaction handling.
240
241
```typescript { .api }
242
/**
243
* Floating content overlay with positioning
244
* @param activator - Trigger element
245
* @param active - Popover visibility state
246
* @param onClose - Close handler
247
* @param children - Popover content
248
* @returns JSX element with popover overlay
249
*/
250
function Popover(props: PopoverProps): JSX.Element;
251
252
interface PopoverProps {
253
/** Popover visibility */
254
active: boolean;
255
/** Trigger element */
256
activator: React.ReactNode;
257
/** Close handler */
258
onClose: () => void;
259
/** Popover content */
260
children?: React.ReactNode;
261
/** Preferred position */
262
preferredPosition?: 'above' | 'below' | 'mostSpace';
263
/** Preferred alignment */
264
preferredAlignment?: 'left' | 'center' | 'right';
265
/** Full width */
266
fullWidth?: boolean;
267
/** Full height */
268
fullHeight?: boolean;
269
/** Prevent autofocus */
270
preventAutofocus?: boolean;
271
/** Sectioned content */
272
sectioned?: boolean;
273
/** Hide on external click */
274
hideOnPrint?: boolean;
275
/** Popover ID */
276
id?: string;
277
/** Autofocus target */
278
autofocusTarget?: PopoverAutofocusTarget;
279
/** Prevent close on child overlay click */
280
preventCloseOnChildOverlayClick?: boolean;
281
/** Capture overflow */
282
captureOverscroll?: boolean;
283
/** Fixed position */
284
fixed?: boolean;
285
/** Preferred input position */
286
preferInputActivator?: boolean;
287
/** Z-index layer */
288
zIndexOverride?: number;
289
}
290
291
/** Popover autofocus target options */
292
enum PopoverAutofocusTarget {
293
Container = 'container',
294
FirstNode = 'first-node',
295
None = 'none',
296
}
297
298
/** Popover close source tracking */
299
enum PopoverCloseSource {
300
Click = 'click',
301
EscapeKeypress = 'escape-keypress',
302
FocusOut = 'focus-out',
303
ScrollOut = 'scroll-out',
304
}
305
306
interface PopoverPublicAPI {
307
/** Close the popover */
308
close: (source?: PopoverCloseSource) => void;
309
/** Force update position */
310
forceUpdatePosition: () => void;
311
}
312
```
313
314
### Sheet
315
316
Mobile-friendly modal sheet component that slides up from the bottom of the screen.
317
318
```typescript { .api }
319
/**
320
* Mobile-friendly modal sheet sliding from bottom
321
* @param open - Sheet visibility state
322
* @param onClose - Close handler
323
* @param children - Sheet content
324
* @returns JSX element with modal sheet
325
*/
326
function Sheet(props: SheetProps): JSX.Element;
327
328
interface SheetProps {
329
/** Sheet visibility */
330
open: boolean;
331
/** Close handler */
332
onClose: () => void;
333
/** Sheet content */
334
children: React.ReactNode;
335
/** Accessibility label */
336
accessibilityLabel?: string;
337
/** Prevent closing on backdrop click */
338
closing?: boolean;
339
}
340
```
341
342
### Tooltip
343
344
Hover tooltip component providing contextual information and help text for interactive elements.
345
346
```typescript { .api }
347
/**
348
* Hover tooltip for contextual information
349
* @param children - Trigger element
350
* @param content - Tooltip content
351
* @param active - Tooltip visibility override
352
* @returns JSX element with tooltip
353
*/
354
function Tooltip(props: TooltipProps): JSX.Element;
355
356
interface TooltipProps {
357
/** Trigger element */
358
children: React.ReactNode;
359
/** Tooltip content */
360
content: React.ReactNode;
361
/** Manual visibility control */
362
active?: boolean;
363
/** Dismiss callback */
364
onClose?: () => void;
365
/** Preferred position */
366
preferredPosition?: 'above' | 'below' | 'mostSpace';
367
/** Hover delay in milliseconds */
368
hoverDelay?: number;
369
/** Dismissible on outside interaction */
370
dismissOnMouseOut?: boolean;
371
/** Width constraint */
372
width?: 'default' | 'wide';
373
/** Persist on click */
374
persistOnClick?: boolean;
375
/** Z-index override */
376
zIndexOverride?: number;
377
/** Border radius */
378
borderRadius?: BorderRadiusScale;
379
/** Padding override */
380
padding?: SpaceScale;
381
/** Has underline for trigger */
382
hasUnderline?: boolean;
383
/** Accessibility label */
384
accessibilityLabel?: string;
385
}
386
```
387
388
### Backdrop
389
390
Overlay backdrop component for modals and other overlay content with interaction handling.
391
392
```typescript { .api }
393
/**
394
* Overlay backdrop for modals and overlays
395
* @param onClick - Backdrop click handler
396
* @param transparent - Transparent backdrop
397
* @returns JSX element with backdrop overlay
398
*/
399
function Backdrop(props: BackdropProps): JSX.Element;
400
401
interface BackdropProps {
402
/** Backdrop click handler */
403
onClick?: () => void;
404
/** Transparent backdrop */
405
transparent?: boolean;
406
/** Background color override */
407
backgroundColor?: string;
408
/** Z-index override */
409
zIndex?: number;
410
/** Backdrop ID */
411
id?: string;
412
}
413
```
414
415
### Loading
416
417
Global loading indicator component for application-wide loading states.
418
419
```typescript { .api }
420
/**
421
* Global loading indicator
422
* @param loading - Loading state
423
* @returns JSX element with loading indicator
424
*/
425
function Loading(props: LoadingProps): JSX.Element;
426
427
interface LoadingProps {
428
/** Loading state visibility */
429
loading?: boolean;
430
}
431
```
432
433
### Spinner
434
435
Loading spinner component with size options for local loading states.
436
437
```typescript { .api }
438
/**
439
* Loading spinner for local loading states
440
* @param size - Spinner size
441
* @param accessibilityLabel - Accessibility label
442
* @returns JSX element with spinner
443
*/
444
function Spinner(props: SpinnerProps): JSX.Element;
445
446
interface SpinnerProps {
447
/** Spinner size */
448
size?: 'small' | 'large';
449
/** Accessibility label */
450
accessibilityLabel?: string;
451
/** Color override */
452
color?: 'teal' | 'inkLightest';
453
/** Focus management */
454
hasFocusableParent?: boolean;
455
}
456
```
457
458
### ProgressBar
459
460
Progress indicator bar for showing completion status of tasks and processes.
461
462
```typescript { .api }
463
/**
464
* Progress indicator bar
465
* @param progress - Progress percentage (0-100)
466
* @param size - Progress bar size
467
* @returns JSX element with progress bar
468
*/
469
function ProgressBar(props: ProgressBarProps): JSX.Element;
470
471
interface ProgressBarProps {
472
/** Progress percentage (0-100) */
473
progress?: number;
474
/** Progress bar size */
475
size?: 'small' | 'medium' | 'large';
476
/** Color tone */
477
tone?: 'highlight' | 'primary' | 'success' | 'critical';
478
/** Animated progress */
479
animated?: boolean;
480
/** Accessibility label */
481
ariaLabel?: string;
482
}
483
```
484
485
**Usage Example:**
486
487
```typescript
488
import React, { useState } from 'react';
489
import {
490
Banner,
491
Toast,
492
Popover,
493
Button,
494
ActionList,
495
Frame
496
} from '@shopify/polaris';
497
498
function FeedbackExample() {
499
const [toastActive, setToastActive] = useState(false);
500
const [popoverActive, setPopoverActive] = useState(false);
501
502
const toastMarkup = toastActive ? (
503
<Toast
504
content="Product saved successfully"
505
onDismiss={() => setToastActive(false)}
506
/>
507
) : null;
508
509
const activator = (
510
<Button
511
onClick={() => setPopoverActive(!popoverActive)}
512
disclosure
513
>
514
More actions
515
</Button>
516
);
517
518
return (
519
<Frame>
520
<Banner
521
title="Your store is ready to accept payments"
522
tone="success"
523
action={{
524
content: 'Complete setup',
525
onAction: () => console.log('Setup clicked'),
526
}}
527
onDismiss={() => console.log('Banner dismissed')}
528
>
529
<p>Your payment provider has been configured and is ready to process transactions.</p>
530
</Banner>
531
532
<Popover
533
active={popoverActive}
534
activator={activator}
535
onClose={() => setPopoverActive(false)}
536
>
537
<ActionList
538
items={[
539
{
540
content: 'Duplicate',
541
icon: 'duplicate',
542
onAction: () => console.log('Duplicate'),
543
},
544
{
545
content: 'Archive',
546
icon: 'archive',
547
onAction: () => console.log('Archive'),
548
},
549
]}
550
/>
551
</Popover>
552
553
<Button onClick={() => setToastActive(true)}>
554
Show toast
555
</Button>
556
557
{toastMarkup}
558
</Frame>
559
);
560
}
561
```
562
563
## Feedback Constants
564
565
```typescript { .api }
566
/** Default toast display duration in milliseconds */
567
const DEFAULT_TOAST_DURATION: number = 5000;
568
569
/** Default toast duration when action is present in milliseconds */
570
const DEFAULT_TOAST_DURATION_WITH_ACTION: number = 10000;
571
```
572
573
## Shared Feedback Types
574
575
```typescript { .api }
576
/** Icon source type */
577
type IconSource = React.ComponentType<any> | 'placeholder' | string;
578
579
/** Border radius scale for tooltips and overlays */
580
type BorderRadiusScale = '050' | '100' | '150' | '200' | '300' | '400' | '500' | '750';
581
582
/** Space scale for padding and spacing */
583
type SpaceScale =
584
| '025' | '050' | '100' | '150' | '200' | '300' | '400' | '500' | '600'
585
| '800' | '1000' | '1200' | '1600' | '2000' | '2400' | '2800' | '3200';
586
587
/** Action interface for feedback components */
588
interface Action {
589
/** Action content */
590
content?: string;
591
/** Accessibility label */
592
accessibilityLabel?: string;
593
/** Action URL */
594
url?: string;
595
/** External link */
596
external?: boolean;
597
/** Action callback */
598
onAction?(): void;
599
}
600
```
601
602
### Portal
603
604
Low-level component for rendering content outside the normal DOM hierarchy, typically used for overlays and modals.
605
606
```typescript { .api }
607
/**
608
* Renders content outside normal DOM hierarchy
609
* @param children - Content to render in portal
610
* @param idPrefix - Prefix for portal container ID
611
* @param onPortalCreated - Callback when portal is created
612
* @returns JSX element rendered in portal
613
*/
614
function Portal(props: PortalProps): JSX.Element;
615
616
interface PortalProps {
617
/** The content to render inside the portal */
618
children?: React.ReactNode;
619
/** The ID prefix for the portal */
620
idPrefix?: string;
621
/** Callback when the portal is created */
622
onPortalCreated?(): void;
623
}
624
```
625
626
### PortalsManager
627
628
Manager component for coordinating multiple portals and their z-index stacking order.
629
630
```typescript { .api }
631
/**
632
* Manages multiple portals and their stacking order
633
* @param children - Content that may create portals
634
* @returns JSX element with portal management
635
*/
636
function PortalsManager(props: PortalsManagerProps): JSX.Element;
637
638
interface PortalsManagerProps {
639
/** The content to wrap with portal management */
640
children: React.ReactNode;
641
}
642
```
643
644
### PositionedOverlay
645
646
Advanced positioning component for overlays that automatically calculates optimal placement relative to an activator element.
647
648
```typescript { .api }
649
/**
650
* Positions overlay content relative to an activator element
651
* @param active - Whether overlay is visible
652
* @param activator - Element that triggers the overlay
653
* @param children - Overlay content
654
* @param onClose - Close handler
655
* @returns JSX element with positioned overlay
656
*/
657
function PositionedOverlay(props: PositionedOverlayProps): JSX.Element;
658
659
interface PositionedOverlayProps {
660
/** Whether the overlay is active */
661
active: boolean;
662
/** The element that the overlay is positioned relative to */
663
activator: HTMLElement;
664
/** The preferred direction to open */
665
preferredPosition?: 'above' | 'below' | 'mostSpace';
666
/** The preferred alignment of the overlay */
667
preferredAlignment?: 'left' | 'center' | 'right';
668
/** The content of the overlay */
669
children?: React.ReactNode;
670
/** Callback when the overlay should be closed */
671
onClose?(): void;
672
/** Whether to render the overlay with a fixed position */
673
fixed?: boolean;
674
/** The minimum width of the overlay */
675
minWidth?: number;
676
/** Whether to render a full width overlay */
677
fullWidth?: boolean;
678
/** Whether to render the overlay with full height */
679
fullHeight?: boolean;
680
}
681
```
682
683
### ContextualSaveBar
684
685
Save bar that appears when forms have unsaved changes, providing save and discard actions in a prominent banner.
686
687
```typescript { .api }
688
/**
689
* Save bar for forms with unsaved changes
690
* @param message - Message about unsaved changes
691
* @param saveAction - Primary save action
692
* @param discardAction - Discard changes action
693
* @returns JSX element with contextual save bar
694
*/
695
function ContextualSaveBar(props: ContextualSaveBarProps): JSX.Element;
696
697
interface ContextualSaveBarProps {
698
/** Provide a message about the save bar */
699
message?: string;
700
/** Save or commit contextual save bar */
701
saveAction?: ComplexAction;
702
/** Discard or cancel contextual save bar */
703
discardAction?: ComplexAction;
704
/** Remove the normal max-width on the contextual save bar */
705
fullWidth?: boolean;
706
/** Whether the save bar is visible */
707
visible?: boolean;
708
}
709
```
710
711
### Focus
712
713
Focus management component for controlling keyboard navigation and programmatic focus behavior.
714
715
```typescript { .api }
716
/**
717
* Focus management component for keyboard navigation
718
* @param children - Content to manage focus within
719
* @param disabled - Whether focus management is disabled
720
* @returns JSX element with focus management
721
*/
722
function Focus(props: FocusProps): JSX.Element;
723
724
interface FocusProps {
725
/** The content to focus */
726
children?: React.ReactNode;
727
/** Whether the focus is disabled */
728
disabled?: boolean;
729
/** The root element to focus */
730
root?: HTMLElement;
731
}
732
```
733
734
### EventListener
735
736
Component for declaratively adding event listeners with proper cleanup and React lifecycle integration.
737
738
```typescript { .api }
739
/**
740
* Declarative event listener with automatic cleanup
741
* @param event - Event name to listen for
742
* @param handler - Event handler function
743
* @param capture - Use capture phase
744
* @returns JSX element managing event listener
745
*/
746
function EventListener(props: EventListenerProps): JSX.Element;
747
748
interface EventListenerProps {
749
/** The event to listen for */
750
event: string;
751
/** The handler for the event */
752
handler(event: Event): void;
753
/** Whether to capture the event */
754
capture?: boolean;
755
/** Whether the listener is passive */
756
passive?: boolean;
757
/** Whether the listener should be attached once */
758
once?: boolean;
759
}
760
```
761
762
### KeypressListener
763
764
Specialized event listener for keyboard interactions with support for key combinations and modifiers.
765
766
```typescript { .api }
767
/**
768
* Keyboard event listener with key combination support
769
* @param keyCode - Key code to listen for
770
* @param handler - Keypress handler function
771
* @param keyEvent - Type of key event
772
* @returns JSX element managing keypress listener
773
*/
774
function KeypressListener(props: KeypressListenerProps): JSX.Element;
775
776
interface KeypressListenerProps {
777
/** The key code to listen for */
778
keyCode: Key;
779
/** The handler for the keypress */
780
handler(event: KeyboardEvent): void;
781
/** The type of key event to listen for */
782
keyEvent?: 'keydown' | 'keyup';
783
}
784
```
785
786
### ScrollLock
787
788
Component that prevents body scrolling when active, typically used with modals and overlays.
789
790
```typescript { .api }
791
/**
792
* Prevents body scrolling when active
793
* @param children - Content to render while scroll is locked
794
* @returns JSX element with scroll lock behavior
795
*/
796
function ScrollLock(props?: { children?: React.ReactNode }): JSX.Element;
797
```
798
799
### TrapFocus
800
801
Focus trap component that constrains keyboard navigation within its content, essential for modal accessibility.
802
803
```typescript { .api }
804
/**
805
* Constrains keyboard focus within its content
806
* @param children - Content to trap focus within
807
* @param trapping - Whether focus trapping is active
808
* @returns JSX element with focus trapping
809
*/
810
function TrapFocus(props: TrapFocusProps): JSX.Element;
811
812
interface TrapFocusProps {
813
/** Whether to trap focus */
814
trapping?: boolean;
815
/** The content to trap focus within */
816
children?: React.ReactNode;
817
}
818
```