Troubleshooting
Fixes for iOS Safari quirks, video playback, fullscreen, and keyboard navigation.
iOS Safari
Viewport doesn't fill screen / bottom black space
Safari's collapsible address bar makes 100vh taller than the visible area. Use 100dvh:
.slider-container {
height: 100dvh; /* not 100vh */
}Horizontal scroll / content overflow
100vw includes scrollbar width on iOS, pushing content past the edge. Use 100% and lock horizontal overflow:
html, body {
overflow-x: hidden;
}
.slider-container {
width: 100%; /* not 100vw */
}Pull-to-refresh / rubber-band bounce
Safari's pull-to-refresh and elastic bounce fight vertical swipe gestures. Do not put overscroll-behavior: none on html, body. That kills normal page scroll. ReelPlayerOverlay, LightboxOverlay, and StoriesPlayerOverlay handle this on their own containers. For custom layouts, scope it:
.slider-container {
overscroll-behavior: none;
}SwipeToClose downward edge case. Any SwipeToClose with direction="down" (lightbox, stories player, custom overlays) is preempted on iOS Safari — the browser fires pull-to-refresh from the document level before the wrapper sees the touch. The overlay locks body scroll but the browser still owns vertical-pan handling at the root. Scope overscroll-behavior-y: contain on <html> only while the overlay is open and restore on close:
useEffect(() => {
if (!isOpen) return;
const html = document.documentElement;
const prev = html.style.overscrollBehaviorY;
html.style.overscrollBehaviorY = 'contain';
return () => {
html.style.overscrollBehaviorY = prev;
};
}, [isOpen]);Pinch-to-zoom interferes with gestures
Disable zoom to stop pinch and double-tap from firing during swipes:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" />viewport-fit=cover extends your layout into the Dynamic Island / notch safe area.
Layout broken after keyboard dismisses
Safari sometimes leaves the viewport compressed after the keyboard closes. Force a reset on blur:
// React
<input
onBlur={() => {
window.scrollTo(0, 0);
apiRef.current?.adjust();
}}
/>
// Angular
(blur)="onInputBlur()"
onInputBlur() {
window.scrollTo(0, 0);
this.reelApi?.adjust();
}General
Slides render at 0×0 size
Without a size prop, the slider reads its container dimensions through ResizeObserver. A container with no CSS height measures 0×0, so nothing renders. Pass size or give the container dimensions:
/* The parent must have a height for auto-sizing to work */
.slider-container {
width: 100%;
height: 100dvh;
}Video
Video doesn't autoplay
Browsers block unmuted autoplay. ReelKit sets muted and playsInline on every video element. Videos start muted; users unmute with the sound toggle after a tap. Check that you're not overriding these attributes in a custom slide.
Video thumbnail / frame capture is blank
Frame capture draws the video onto a <canvas>. Cross-origin videos taint the canvas, so the draw silently fails. Your video CDN must return CORS headers:
Access-Control-Allow-Origin: *ReelKit sets crossOrigin="anonymous" by default. If you use a custom video element, add it yourself.
Fullscreen
Fullscreen button does nothing on Safari
ReelKit disables the Fullscreen API on Safari. iOS Safari restricts fullscreen to <video> elements only. Desktop Safari breaks position: fixed overlays in fullscreen: elements lose stacking context or vanish. requestFullscreen() resolves as a no-op on Safari.
Keyboard Navigation
Arrow keys don't navigate after providing onNavKeyPress
onNavKeyPress replaces default keyboard navigation. ReelKit stops calling next()/prev() and hands control to you. Call them yourself:
<Reel
onNavKeyPress={(increment) => {
// Your custom logic here
console.log('Nav key:', increment);
// You must trigger navigation yourself:
apiRef.current?.[increment === 1 ? 'next' : 'prev']();
}}
/>Escape key doesn't close the overlay
The keyboard controller handles arrow keys only. ReelPlayerOverlay and LightboxOverlay listen for Escape separately. If you build a custom overlay, add your own Escape handler in onClose.