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:

css
.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:

css
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:

css
.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:

tsx
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:

html
<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:

tsx
// 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:

css
/* 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:

text
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:

tsx
<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.