GitHub

Command Palette

Search for a command to run...

Modal

(A) One-liner

Modal is an overlay modal component that appears at the center of the screen. It controls visibility state externally with visible/onClose, and content is composed with Modal.Header/Body/Description/Footer compound structure. "Close paths" like backdrop click/swipe/Android back button are controlled with closable and related props.


(B) Installation (Track A / Track B)

Track A (CLI / local install)

Track A: add Modal
pnpm dlx @fleet-ui/cli add Modal
Track A: import
import { Modal } from '@fleet-ui/local/components';

Track B (NPM package)

Track B: import
import { Modal } from '@fleet-ui/components';

(Required) Per-component Dependencies

Modal requires these dependencies:

  • expo-blur (backdrop blur)
  • react-native-reanimated, react-native-gesture-handler (animation/swipe dismiss)
  • lucide-react-native (header close icon)
Required deps (Expo)
npx expo install expo-blur
Recommended runtime deps
pnpm add react-native-reanimated react-native-gesture-handler lucide-react-native react-native-unistyles

(D) Core Features & Usage

D-1. Most Common Scenario (Minimal Example)

Modal basic usage
import { useState } from 'react';
import { Text } from 'react-native';
import { Modal, Button } from '@fleet-ui/components';
 
export function Example() {
  const [open, setOpen] = useState(false);
 
  return (
    <>
      <Button onPress={() => setOpen(true)}>Open</Button>
 
      <Modal visible={open} onClose={() => setOpen(false)}>
        <Modal.Header title="Notice" />
        <Modal.Description content="There's an important notice." />
        <Modal.Body>
          <Text>Body content</Text>
        </Modal.Body>
        <Modal.Footer>
          <Button variant="ghost" onPress={() => setOpen(false)}>Close</Button>
          <Button onPress={() => setOpen(false)}>Confirm</Button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

D-2. Close Policy: closable, swipeToDismiss

  • When closable=true:
    • Closes on backdrop click
    • (Optional) Closes on swipe down
    • Closes on Android hardware back button
  • When closable=false:
    • Restricts closing via backdrop/back button (and ESC on web)
    • Close is provided only through explicit paths like buttons

D-3. Swipe Dismiss Details: swipeThreshold

When swipeToDismiss=true, closes under these conditions:

  • Drag distance (translationY) exceeds swipeThreshold, or
  • velocityY > 500

(E) Internal State / Shared Value / Animation

E-1. State Model

Modal controls visible externally and doesn't hold internal state directly. So to close, you must set external state to false in onClose().

E-2. Reanimated / Shared Value

Implementation can animate with these shared values:

  • Backdrop opacity (withTiming)
  • Content translateY (withTiming / withSpring)
  • Sync backdrop opacity with swipe progress (interpolate)

Also handles swipe dismiss gesture with Gesture.Pan().


(F) Accessibility

  • Modal overlay sets modal scope with accessibilityViewIsModal + importantForAccessibility="yes".
  • Handles screen reader "close" gesture with onAccessibilityEscape (when closable).
  • Modal.Header close button should have accessibilityRole="button" + accessibilityLabel="Close modal".

Recommended rules:

  • Close button label can be customized with app i18n policy via custom header.
  • (Web) Verify ESC/focus navigation is natural for keyboard users.

(G) Props Table

Reference: Public types from packages/components/src/Modal/Modal.types.ts.

PropTypeRequiredDefaultDescriptionPlatform notes
visiblebooleanYes-Modal visibility stateExternal control
onClose() => voidYes-Close callback (change external state to false)Required
size'sm' | 'md' | 'lg'No'md'Content max width/padding preset
colorScheme'base' | 'inverted'No'base'Color theme
rounded'none' | '_2xs' | 'xs' | 'sm' | 'md' | 'lg'No'md'Round preset
closablebooleanNotrueAllow close via backdrop/swipe/back button
swipeToDismissbooleanNotrueEnable swipe dismissWorks with closable
swipeThresholdnumberNo100Swipe dismiss threshold (px)
backdropOpacitynumberNo0.5(types) / impl default may be 0.3Backdrop opacityNote difference between impl default and type annotation
useBackdropBlurbooleanNotrueUse backdrop blurexpo-blur required
backdropBlurIntensitynumberNo50Blur intensity (0~100)
backdropBlurTint'light' | 'dark'No'light'Blur tint
modalPropsOmit<RNModalProps, 'visible' | 'children' | 'transparent' | 'animationType'>No-RN Modal props overrideSome props are fixed internally
contentStyleStyleProp<ViewStyle>No-Content style override
backdropStyleStyleProp<ViewStyle>No-Backdrop style override
onShow() => voidNo-Show callback
onDismiss() => voidNo-Dismiss callback
childrenReactNodeYes-Modal content
  • Modal.Header: ModalHeaderProps (title?, showCloseButton?, children?, titleStyle?)
  • Modal.Body: ModalBodyProps (children)
  • Modal.Description: ModalDescriptionProps (content, contentStyle?, containerStyle?)
  • Modal.Footer: ModalFooterProps (children)