Skip to content

sivantha96/react-native-trays

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

22 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

react-native-trays

Demo 1 Demo 2 Demo 3

A production-grade, fully open-source tray system for React Native with a React Navigation-like API. Built with TypeScript, Reanimated, and best industry practices, supporting both Expo and bare workflows.


npm version Downloads License: MIT Build Status

Table of Contents



✨ Features

  • React Navigation-like API: Familiar hooks-based API with push, pop, replace, and dismiss operations
  • Multiple tray stacks: Create independent tray flows with separate configurations
  • Customizable animations: Built-in Reanimated animations (slide, fade) with support for custom animations
  • Keyboard awareness: Trays automatically adjust position when keyboard appears
  • Safe area support: Proper handling of device notches and system UI
  • Backdrop customization: Blur effects, opacity, and dismiss behavior
  • Full TypeScript support: Complete type safety with generics for tray props
  • ID and key-based operations: Target specific trays or tray types
  • Expo and bare workflow compatible: Works in all React Native environments
  • Production-ready: Built with performance and reliability in mind

πŸš€ Installation

# Using yarn
yarn add react-native-trays

# Or using npm
npm install react-native-trays

Required Peer Dependencies

The library requires the following peer dependencies:

# Install required peer dependencies
npm install react-native-reanimated react-native-safe-area-context react-native-uuid

# Optional: For backdrop blur effect in Expo
npm install expo-blur

⚠️ Required Setup

Before using the library, you must properly set up the required dependencies:

  1. React Native Reanimated - Follow the official setup guide

    • Make sure to add the Babel plugin to your babel.config.js
    • For Expo, ensure you have the correct version compatible with your Expo SDK
  2. React Native Safe Area Context - Follow the official setup guide

    • Wrap your app with SafeAreaProvider before using TrayProvider

If these dependencies are not correctly set up, you may encounter errors like Native part of Reanimated doesn't seem to be initialized (Worklets).


πŸš€ Quick Start

import { TrayProvider, useTrays } from 'react-native-trays';
import { SafeAreaProvider } from 'react-native-safe-area-context';

// Define your tray components
const trays = {
  MyTray: {
    component: ({ message }) => (
      <View style={{ padding: 20, backgroundColor: 'white', borderRadius: 16 }}>
        <Text>{message}</Text>
        <Button title="Close" onPress={() => {}} />
      </View>
    ),
  },
};

// Wrap your app with providers
export default function App() {
  return (
    <SafeAreaProvider>
      <TrayProvider trays={trays}>
        <HomeScreen />
      </TrayProvider>
    </SafeAreaProvider>
  );
}

// Use trays in your components
function HomeScreen() {
  const { push, pop } = useTrays('main');

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Open Tray"
        onPress={() => push('MyTray', { message: 'Hello from tray!' })}
      />
    </View>
  );
}

πŸ”₯ TypeScript Support

React Native Trays provides full TypeScript support with generics for type-safe tray props.

Defining Tray Props Types

Create a type map for your tray components to ensure type safety:

// Define your tray keys (enum or string literals)
enum TrayEnum {
  Details = 'DetailsTray',
  Form = 'FormTray',
  Settings = 'SettingsTray',
}

// Define props for each tray component
type DetailsTrayProps = {
  id: string;
  title: string;
};

type FormTrayProps = {
  onSubmit: (data: any) => void;
  initialValues?: Record<string, any>;
};

type SettingsTrayProps = {
  userId: string;
  preferences: Record<string, boolean>;
};

// Create a type map for all tray props
type TrayProps = {
  [TrayEnum.Details]: DetailsTrayProps;
  [TrayEnum.Form]: FormTrayProps;
  [TrayEnum.Settings]: SettingsTrayProps;
};

Using Type-Safe Hooks

Pass your type map to the useTrays hook for complete type safety:

import { useTrays } from 'react-native-trays';

function MyComponent() {
  // Type-safe hook with your TrayProps type map
  const { push, pop, replace } = useTrays<TrayProps>('main');

  // TypeScript will enforce correct props for each tray
  const openDetailsTray = () => {
    push(TrayEnum.Details, {
      id: '123',
      title: 'Product Details',
      // TypeScript error: Property 'invalid' does not exist on type 'DetailsTrayProps'
      // invalid: true,
    });
  };

  // Type-safe replace operation
  const updateForm = (newValues: Record<string, any>) => {
    replace(TrayEnum.Form, {
      onSubmit: handleSubmit,
      initialValues: newValues,
    });
  };

  return <Button title="Open Details" onPress={openDetailsTray} />;
}

πŸ“š API Reference

See API.md for a complete reference of all types, hooks, and provider props.


πŸ€— Advanced Usage

Multiple Tray Stacks

Create independent tray flows with separate configurations:

// Configure different stack configurations
const stackConfigs = {
  main: {
    backdropStyles: { backgroundColor: 'rgba(0,0,0,0.5)' },
    trayStyles: { backgroundColor: 'white', borderRadius: 16 },
  },
  modal: {
    backdropStyles: { backgroundColor: 'rgba(0,0,0,0.7)' },
    dismissOnBackdropPress: false,
  },
};

// In your component
function MyComponent() {
  const mainTrays = useTrays<MainTrayProps>('main');
  const modalTrays = useTrays<ModalTrayProps>('modal');

  return (
    <View>
      <Button title="Open Main Tray" onPress={() => mainTrays.push('InfoTray', { ... })} />
      <Button title="Open Modal Tray" onPress={() => modalTrays.push('AlertTray', { ... })} />
    </View>
  );
}

Custom Animations

Use Reanimated's animation builders for custom entry and exit animations:

import {
  SlideInUp,
  SlideOutDown,
  FadeIn,
  FadeOut,
} from 'react-native-reanimated';

<TrayProvider
  trays={trays}
  stackConfigs={{
    main: {
      enteringAnimation: SlideInUp.springify().damping(15),
      exitingAnimation: SlideOutDown.duration(300),
    },
    modal: {
      enteringAnimation: FadeIn.duration(400),
      exitingAnimation: FadeOut.duration(300),
    },
  }}
>
  <App />
</TrayProvider>;

Keyboard Awareness

Trays automatically adjust when the keyboard appears:

<TrayProvider
  trays={trays}
  stackConfigs={{
    main: {
      adjustForKeyboard: true, // default is true
    },
  }}
>
  <App />
</TrayProvider>

πŸ§‘β€πŸ’» Live Demo

Try the library instantly on Expo Snack or check EXPO_SNACK.md for more details.


🀝 Contributing

PRs and issues are welcome! Please see CONTRIBUTING.md and CODE_OF_CONDUCT.md.


πŸ“„ License

MIT Β© Sivantha

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •