import { Overlay, Button } from 'reshaped';
function Example() {
const [active, setActive] = React.useState(false);
return (
<>
<Button onClick={() => setActive(true)}>Show Overlay</Button>
<Overlay
active={active}
onClose={() => setActive(false)}
>
<View
padding={6}
backgroundColor="neutral"
borderRadius="medium"
>
<Text>Overlay content</Text>
<Button onClick={() => setActive(false)}>Close</Button>
</View>
</Overlay>
</>
);
}
Usage
Overlay is a low-level component for creating full-screen overlay containers. It handles portal rendering, animations, click-outside detection, and scroll locking. Use Modal for most dialog use cases.
Props
Controls the visibility of the overlay.<Overlay active={isOpen} onClose={() => setIsOpen(false)}>
children
React.ReactNode | (props: { active: boolean }) => React.ReactNode
Content of the overlay. Can be a render function that receives the active state.<Overlay active={active}>
<View>Content</View>
</Overlay>
<Overlay active={active}>
{({ active }) => <View opacity={active ? 1 : 0}>Content</View>}
</Overlay>
Make the overlay background transparent. Pass a number (0-1) for custom opacity.<Overlay transparent> {/* Fully transparent */}
<Overlay transparent={0.5}> {/* 50% opacity */}
Apply blur effect to the overlay background.
Control overflow behavior of the content.Options: "auto", "hidden"Default: "auto"
onClose
(args: { reason: string }) => void
Callback when the overlay is closed. Reason can be:
"overlay-click": User clicked on the overlay background
"escape-key": User pressed Escape
<Overlay
onClose={({ reason }) => {
console.log('Closed via:', reason);
setActive(false);
}}
/>
Callback when the overlay opens.
Callback after the open animation completes.
Callback after the close animation completes.
Prevent closing when clicking on the overlay background.<Overlay disableCloseOnClick>
containerRef
React.RefObject<HTMLElement>
Container element to render the overlay within.const containerRef = React.useRef();
<View attributes={{ ref: containerRef }}>
<Overlay containerRef={containerRef}>
</View>
Contain the overlay within the container bounds. Defaults to true when containerRef is provided.
Additional CSS class for the root element.
Additional HTML attributes for the root element.
Examples
Basic Overlay
function BasicOverlay() {
const [active, setActive] = React.useState(false);
return (
<>
<Button onClick={() => setActive(true)}>Open</Button>
<Overlay active={active} onClose={() => setActive(false)}>
<View
align="center"
justify="center"
height="100%"
onClick={() => setActive(false)}
>
<View
padding={6}
backgroundColor="neutral"
borderRadius="medium"
onClick={(e) => e.stopPropagation()}
>
<Text variant="title-5">Overlay Content</Text>
<Button onClick={() => setActive(false)}>Close</Button>
</View>
</View>
</Overlay>
</>
);
}
Transparent Overlay
<Overlay transparent active={active} onClose={handleClose}>
<View align="center" justify="center" height="100%">
<View padding={6} backgroundColor="neutral" borderRadius="medium">
<Text>Content on transparent overlay</Text>
</View>
</View>
</Overlay>
Blurred Overlay
<Overlay blurred active={active} onClose={handleClose}>
<View align="center" justify="center" height="100%">
<View padding={6} backgroundColor="neutral" borderRadius="medium">
<Text>Content with blurred background</Text>
</View>
</View>
</Overlay>
Custom Opacity
<Overlay transparent={0.3} active={active} onClose={handleClose}>
<View align="center" justify="center" height="100%">
<Text>30% opacity overlay</Text>
</View>
</Overlay>
Lightbox
function Lightbox({ images, initialIndex = 0 }) {
const [active, setActive] = React.useState(false);
const [currentIndex, setCurrentIndex] = React.useState(initialIndex);
return (
<>
<Button onClick={() => setActive(true)}>View Gallery</Button>
<Overlay
active={active}
onClose={() => setActive(false)}
transparent={0.95}
>
<View height="100%" align="center" justify="center" gap={4}>
<View position="relative">
<Image
src={images[currentIndex]}
attributes={{
style: { maxHeight: '80vh', maxWidth: '90vw' },
}}
/>
</View>
<View direction="row" gap={2}>
<Button
onClick={() => setCurrentIndex(Math.max(0, currentIndex - 1))}
disabled={currentIndex === 0}
>
Previous
</Button>
<Button
onClick={() => setCurrentIndex(Math.min(images.length - 1, currentIndex + 1))}
disabled={currentIndex === images.length - 1}
>
Next
</Button>
<Button onClick={() => setActive(false)}>
Close
</Button>
</View>
</View>
</Overlay>
</>
);
}
Loading Overlay
function LoadingOverlay({ active }) {
return (
<Overlay
active={active}
transparent={0.5}
disableCloseOnClick
>
<View
height="100%"
align="center"
justify="center"
onClick={(e) => e.stopPropagation()}
>
<View gap={3} align="center">
<Loader size="large" color="primary" ariaLabel="Loading" />
<Text variant="body-2">Processing...</Text>
</View>
</View>
</Overlay>
);
}
Render Props Pattern
<Overlay active={active}>
{({ active }) => (
<View
height="100%"
align="center"
justify="center"
attributes={{
style: {
opacity: active ? 1 : 0,
transform: active ? 'scale(1)' : 'scale(0.95)',
transition: 'all 0.2s',
},
}}
>
<Text>Animated content</Text>
</View>
)}
</Overlay>
Contained Overlay
function ContainedOverlay() {
const containerRef = React.useRef();
const [active, setActive] = React.useState(false);
return (
<View
attributes={{ ref: containerRef }}
height="400px"
position="relative"
overflow="hidden"
borderRadius="medium"
backgroundColor="neutral-faded"
>
<View padding={4}>
<Button onClick={() => setActive(true)}>
Open Overlay
</Button>
</View>
<Overlay
containerRef={containerRef}
active={active}
onClose={() => setActive(false)}
>
<View height="100%" align="center" justify="center">
<View padding={4} backgroundColor="neutral" borderRadius="medium">
<Text>Contained within parent</Text>
<Button onClick={() => setActive(false)}>Close</Button>
</View>
</View>
</Overlay>
</View>
);
}
Video Player Overlay
function VideoOverlay({ videoUrl }) {
const [active, setActive] = React.useState(false);
return (
<>
<Button onClick={() => setActive(true)} icon={IconPlay}>
Watch Video
</Button>
<Overlay
active={active}
onClose={() => setActive(false)}
transparent
>
<View height="100%" align="center" justify="center" padding={4}>
<View
width="100%"
attributes={{ style: { maxWidth: '1200px' } }}
>
<video
src={videoUrl}
controls
autoPlay
style={{ width: '100%', borderRadius: '8px' }}
/>
</View>
</View>
</Overlay>
</>
);
}
Custom Close Button
function OverlayWithClose() {
const [active, setActive] = React.useState(false);
return (
<Overlay
active={active}
onClose={() => setActive(false)}
>
<View height="100%" align="center" justify="center">
<View position="relative" padding={6} backgroundColor="neutral" borderRadius="medium">
<Button
variant="ghost"
icon={IconX}
onClick={() => setActive(false)}
attributes={{
style: {
position: 'absolute',
top: '8px',
right: '8px',
},
}}
/>
<View gap={3}>
<Text variant="title-5">Content</Text>
<Text>Overlay content here</Text>
</View>
</View>
</View>
</Overlay>
);
}
Prevent Close
function PersistentOverlay() {
const [active, setActive] = React.useState(false);
return (
<Overlay
active={active}
disableCloseOnClick
onClose={({ reason }) => {
// Only allow close via button, not Escape or outside click
if (reason !== 'escape-key' && reason !== 'overlay-click') {
setActive(false);
}
}}
>
<View height="100%" align="center" justify="center">
<View padding={6} backgroundColor="neutral" borderRadius="medium">
<Text>Click the button to close</Text>
<Button onClick={() => setActive(false)}>Close</Button>
</View>
</View>
</Overlay>
);
}
When to Use
Use Overlay for:
- Building custom modal-like components
- Lightboxes and media viewers
- Full-screen loading states
- Custom overlay patterns not covered by Modal
Don’t use Overlay for:
- Standard dialogs (use Modal)
- Floating menus (use DropdownMenu)
- Tooltips (use Tooltip)
- Popovers (use Popover)
Accessibility
- Locks body scroll when active
- Pressing Escape closes the overlay
- Clicking outside closes by default
- Provide proper focus management in your content
- Add appropriate ARIA attributes via
attributes
- Ensure keyboard accessibility for interactive content