react-spring-bottom-sheet

repository·main·Indexed 21 days ago

https://github.com/stipsan/react-spring-bottom-sheet

An accessible, performant, and highly animated bottom sheet component for React, built on react-spring and react-use-gesture. It features customizable snap points, focus trapping, background scroll locking, and a comprehensive set of spring animation lifecycle events (onSpringStart, onSpringCancel, onSpringEnd). Supports TypeScript via the BottomSheetRef type for imperative control using the snapTo method.

Tokens
6.5K
Snippets
18
Records
32
Agent score
75%

What's inside react-spring-bottom-sheet

  1. Understand SpringEvent types and sources

    main

    All event handlers receive a SpringEvent object. The type property is always present and can be 'OPEN' | 'RESIZE' | 'SNAP' | 'CLOSE'. Some event types include a source property to provide context:

    RESIZE source

    • 'window': Triggered by a window resize event.
    • 'maxheightprop': Triggered when the maxHeight prop changes.
    • 'element': Triggered when header, footer, or content resize observers detect a change.

    SNAP source

    • 'dragging': Triggered when a drag gesture ends.
    • 'custom': Triggered when using the ref.current.snapTo() method.
    • string: A custom source string provided by the user.
  2. Configure PostCSS for Custom CSS fallback

    main

    To use custom CSS effectively, it is recommended to copy style.css into your project and use postcss-custom-properties-fallback to ensure default variables are available.

    module.exports = {
      plugins: {
        // Ensures the default variables are available
        'postcss-custom-properties-fallback': {
          importFrom: require.resolve('react-spring-bottom-sheet/defaults.json'),
        },
      },
    }
  3. Implement Custom CSS with PostCSS fallback

    main

    For advanced customization, it is recommended to copy the project's style.css into your own project. To ensure default variables are available, you can use postcss-custom-properties-fallback in your postcss.config.js setup.

    module.exports = {
      plugins: {
        // Ensures the default variables are available
        'postcss-custom-properties-fallback': {
          importFrom: require.resolve('react-spring-bottom-sheet/defaults.json'),
        },
      },
    }
  4. Customize the look using CSS Custom Properties

    main

    You can customize the appearance of the bottom sheet by overriding the following CSS custom properties in your :root or component scope:

    :root {
      --rsbs-backdrop-bg: rgba(0, 0, 0, 0.6);
      --rsbs-bg: #fff;
      --rsbs-handle-bg: hsla(0, 0%, 0%, 0.14);
      --rsbs-max-w: auto;
      --rsbs-ml: env(safe-area-inset-left);
      --rsbs-mr: env(safe-area-inset-right);
      --rsbs-overlay-rounded: 16px;
    }
  5. Use BottomSheet with TypeScript

    main

    TypeScript support is built-in. If you need to programmatically control the sheet's position using the snapTo method, use the BottomSheetRef type for your useRef hook.

    import { useRef } from 'react'
    import { BottomSheet, BottomSheetRef } from 'react-spring-bottom-sheet'
    
    export default function Example() {
      const sheetRef = useRef<BottomSheetRef>()
      return (
        <BottomSheet open ref={sheetRef}>
          <button
            onClick={() => {
              // Full typing for the arguments available in snapTo
              sheetRef.current.snapTo(({ maxHeight }) => maxHeight)
            }}
          >
            Expand to full height
          </button>
        </BottomSheet>
      )
    }
  6. Basic usage of BottomSheet

    main

    To use the BottomSheet component, import it along with its CSS. The component is controlled via the open prop. When open is false, the component unmounts its children to ensure accessibility (ARIA) and prevent screen readers from interacting with hidden content.

    import { useState } from 'react'
    import { BottomSheet } from 'react-spring-bottom-sheet'
    
    // Import the required CSS
    import 'react-spring-bottom-sheet/dist/style.css'
    
    export default function Example() {
      const [open, setOpen] = useState(false)
      return (
        <>
          <button onClick={() => setOpen(true)}>Open</button>
          <BottomSheet open={open}>My awesome content here</BottomSheet>
        </>
      )
    }
  7. Handle Bottom Sheet events with onSpringStart

    main

    The onSpringStart event is fired during OPEN, RESIZE, SNAP, or CLOSE transitions. It accepts a SpringEvent object where the type property indicates the transition type.

    Key Feature: Delaying Transitions You can use an async function or return a Promise within onSpringStart to delay the animation. This is useful for fetching data or showing a loading spinner before the sheet actually begins its transition.

    function Example() {
      const [data, setData] = useState([])
      return (
        <BottomSheet
          onSpringStart={async (event) => {
            if (event.type === 'OPEN') {
              // the bottom sheet gently waits
              const data = await fetch(/* . . . */)
              setData(data)
              // and now we can proceed
            }
          }}
        >
          {data.map(/* . . . */)}
        </BottomSheet>
      )
    }
  8. Handle Bottom Sheet events with onSpringEnd

    main
    The onSpringEnd event is fired after a transition completes. For CLOSE transitions, this event provides a hook after the sheet has cleaned up its internal state (like body-scroll-lock and focus-trap) but before the component unmounts. This is useful for performing cleanup logic that must happen before unmounting.
  9. Configure defaultSnap for initial position

    main

    The defaultSnap prop determines the sheet's position when it first opens. It can be a number or a callback function. The callback receives the same state as snapPoints, plus snapPoints (the array of available points) and lastSnap (the last position used).

    <BottomSheet
      // the first snap point height depends on the content, while the second one is equivalent to 60vh
      snapPoints={({ minHeight, maxHeight }) => [minHeight, maxHeight / 0.6]}
      // Opens the largest snap point by default, unless the user selected one previously
      defaultSnap={({ lastSnap, snapPoints }) =>
        lastSnap ?? Math.max(...snapPoints)
      }
    />
  10. Access the current sheet height via ref

    main

    The ref.current.height property provides the current snap point (height in pixels) of the bottom sheet.

    Note: This value is updated outside of the React render cycle for performance reasons. If you need to log or use the height during a transition, use requestAnimationFrame to ensure you capture the value at the correct moment.

    <BottomSheet
      ref={sheetRef}
      onSpringStart={() => {
        console.log('Transition from:', sheetRef.current.height);
        requestAnimationFrame(() =>
          console.log('Transition to:', sheetRef.current.height)
        )
      }}
      onSpringEnd={() =>
        console.log('Finished transition to:', sheetRef.current.height)
      }
    />
  11. Configure blocking mode for accessibility

    main

    The blocking prop (boolean, default true) controls focus trapping and background visibility.

    • When true: Enables focus trapping (keyboard users cannot tab out of the sheet) and sets aria-hidden on the background page.
    • When false: Disables these behaviors, allowing the sheet to act as a non-blocking overlay (useful for sidebars).