Skip to content

vercel-labs/concentric-sheet

Repository files navigation

Concentric Sheet

demo.mp4

React Native Modal replacement for iOS sheet presentation.

This package keeps React Native's Modal behavior, and adds runtime access to native UIViewController / UISheetPresentationController options such as:

  • Detents (medium, large)
  • Custom detent heights ([78, 350])
  • Grabber visibility
  • Preferred corner radius
  • Selected detent
  • Largest undimmed detent
  • Edge-attached behavior
  • Interactive dismissal lock (isModalInPresentation)
  • Preferred content width/height

Install

bun add concentric-sheet react-native-nitro-modules

Then install iOS pods in your app:

bunx pod-install ./ios

Usage

import React from 'react'
import { View, Text, Button } from 'react-native'
import { Modal } from 'concentric-sheet'

export function Example() {
  const [visible, setVisible] = React.useState(false)

  return (
    <>
      <Button title="Open" onPress={() => setVisible(true)} />
      <Modal
        visible={visible}
        onRequestClose={() => setVisible(false)}
        detents={['medium', 'large']}
        prefersGrabberVisible
        preferredCornerRadius={24}
      >
        <View style={{ flex: 1, padding: 20 }}>
          <Text>Native sheet modal</Text>
          <Button title="Close" onPress={() => setVisible(false)} />
        </View>
      </Modal>
    </>
  )
}

API

Modal accepts all React Native Modal props, plus:

  • detents?: ('medium' | 'large')[]
  • customDetentHeights?: number[] (iOS 16+)
  • selectedDetentIdentifier?: 'medium' | 'large'
  • selectedCustomDetentHeight?: number (iOS 16+)
  • largestUndimmedDetentIdentifier?: 'medium' | 'large'
  • largestUndimmedCustomDetentHeight?: number (iOS 16+)
  • prefersGrabberVisible?: boolean
  • preferredCornerRadius?: number
  • prefersScrollingExpandsWhenScrolledToEdge?: boolean
  • prefersEdgeAttachedInCompactHeight?: boolean
  • widthFollowsPreferredContentSizeWhenEdgeAttached?: boolean
  • isModalInPresentation?: boolean
  • preferredContentWidth?: number
  • preferredContentHeight?: number
  • modalViewBackground?: 'clear' | 'systemBackground'
  • cornerConfiguration?: { type: 'none' | 'fixed' | 'containerConcentric' | 'capsule', radius?: number, minimumRadius?: number, maximumRadius?: number }

Imperative helpers:

  • applyPresentedModalConfig(config)
  • dismissPresentedNativeModal(animated?)

TODO

  • Android support

Contributing

The native Swift implementation lives in ios/SheetModalController.swift.

The Nitro TypeScript API is defined in src/. When you change props or the spec, regenerate the native bridge code by running:

bunx nitrogen

Commit the generated files in nitrogen/ alongside your changes.

DCO

All commits must be signed off per the Developer Certificate of Origin. Use the -s flag when committing:

git commit -s -m "your commit message"

Notes

  • iOS-only native implementation.
  • UISheetPresentationController options require iOS 15+.
  • cornerConfiguration requires iOS 26+.
  • For sheet behavior, use presentationStyle="pageSheet" or formSheet.
  • Setting largestUndimmedDetentIdentifier can disable outside-tap dismissal at/under that detent because the dimming view is removed.

About

An iOS sheet view controller that supports concentric corners + glass

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors