react-native-teleport

repository·main·Indexed 20 days ago

https://github.com/kirillzyusko/react-native-teleport

A library providing native portal and teleportation capabilities for React Native (iOS, Android, and Web). It allows developers to move views across the component tree while preserving React context and tree continuity, enabling advanced UI patterns like pre-rendering expensive components off-screen or reusing views between screens without unmounting.

Tokens
16.2K
Snippets
49
Records
68
Agent score
73%

What's inside react-native-teleport

  1. Overview of react-native-teleport

    main

    react-native-teleport provides a native portal implementation for React Native. It allows you to 'teleport' views across your component tree, enabling seamless transitions and powerful UI patterns by re-parenting views at the native level.

    Key capabilities include:

    • Native View Teleportation: Acts as both a portal and a teleport (re-parenting).
    • Layout Escape: Allows components to escape any layout constraints.
    • React Integration: Preserves React context and maintains React tree continuity.
    • Cross-Platform: Supports iOS, Android, and Web.
    • Performance & Architecture: Native performance, zero dependencies, written in TypeScript, and supports new React Native architectures.
  2. What is react-native-teleport?

    main

    react-native-teleport is a library that provides true native portals for React Native. Unlike JavaScript-based portal libraries that re-parent components in the JS layer, react-native-teleport physically moves the view in the native view hierarchy while keeping the component logically within its original position in the React tree.

    Key Benefits

    • Preserves React Context: Because the component stays in its original React tree position, access to themes, navigation, i18n, and other React Context values is maintained.
    • Native Performance & Layout: Moving the view in the native hierarchy ensures correct z-order, layout behavior, and platform-native performance.
    • Cross-Platform: Works on iOS, Android, and Web.
    • Teleporting: Supports re-parenting existing views without unmounting them, allowing you to move views in native space without breaking React logic.
  3. What is Teleport and why use it?

    main

    The Teleport concept (also known as re-parenting) allows you to move an existing view to a different part of the component hierarchy without unmounting or remounting it.

    Because the component is not unmounted, it preserves its internal state, including:

    • Active animations
    • Scroll positions
    • Video playback progress
    • Input field values

    Common Use Cases:

    • Preserving state: Keeping animations or video playing while moving the view.
    • Advanced transitions: Implementing shared-element-style transitions between screens.
    • Escaping container limits: Moving a view to a top-level overlay while keeping its internal logic alive.
    • Complex UI patterns: Photo galleries, YouTube-like mini players, context menus, app tours, or off-screen pre-rendering.
  4. Key patterns for building Instagram-like shared transitions

    main

    To implement fluid, Instagram-quality shared transitions (e.g., moving a video or image from a feed to a full-screen view), use the following architectural patterns:

    1. Dynamic Teleportation: Use the <Portal> component and dynamically change its hostName prop. This allows you to move a component (like a video player) between different parts of the UI tree (e.g., from a feed to an overlay or a specific screen) without unmounting it, preserving its internal state.
    2. Layout Preservation: Use a fixed-height container in the source location (the feed) to preserve the scroll position of the list while the content is being teleported away.
    3. Modal Presentation: Use transparentModal for navigation. This keeps the underlying screen (the feed) visible during the transition, which is essential for seamless visual continuity and enables gesture-based interactions.
    4. State Coordination: Use a state management tool like Zustand combined with Reanimated SharedValue to coordinate animation states across different components. This ensures the animation logic stays on the UI thread for high performance.
    5. Precise Positioning: Use measureInWindow to capture the exact screen coordinates of the source content. This allows the animation to start from the precise location of the element in the feed and transition smoothly to its new destination.

    This pattern is applicable to any content type, including images, cards, or avatars.

  5. How the 'move' operation enables advanced UI patterns

    main

    While standard React reconciliation uses create, update, and destroy, react-native-teleport introduces a fourth operation: move. This allows you to re-parent components without unmounting or remounting them, which is critical for maintaining state in heavy components (like Video or Lottie).

    Key Use Cases:

    1. Pre-render Components Offscreen

    You can render expensive components off-screen and move them into view only when needed. This avoids setup delays for complex overlays, modals, or bottom sheets.

    // Pre-render the editor off-screen
    <Portal hostName={used ? "editor" : undefined}>
      <ExpensiveRichTextEditor />
    </Portal>
    
    // Later, when user triggers an action, simply move it into view
    // No re-rendering, no state loss, just a native view reposition

    2. Re-use Views Between Screens

    You can render the same component instance on multiple screens and seamlessly move it as users navigate. This enables shared-element-style transitions and avoids the performance cost of re-creating heavy components during navigation transitions.

  6. How Portal and PortalHost work together

    main

    The Portal mechanism relies on a destination container called a PortalHost.

    • <PortalHost />: Acts as the target container where teleported content will be rendered. It is a best practice to declare at least one <PortalHost /> near your application root.
    • <Portal />: The component that wraps the content you want to move. It searches the tree for a matching PortalHost.
    • hostName: A string identifier used to link a specific Portal to a specific PortalHost. This allows you to manage multiple independent layers (e.g., a 'modal' layer and a 'toast' layer) within the same application.
  7. How Portal and PortalHost handle dynamic lifecycle changes

    main

    The library manages transitions between local rendering and teleportation to ensure children survive host changes:

    • Host mounts after Portal: Children move from local rendering into the new host automatically via native re-parenting (no React re-render).
    • Host unmounts while Portal is alive: Children are pulled back to the Portal's local position. They are not destroyed, and all component state, refs, and imperative native state (e.g., video playback, scroll position) are preserved.
    • Host remounts: Children follow the host. A remounted host with the same name picks up exactly where the previous cycle left off.
    • Portal's hostName changes: Children migrate to the new target. If the new target is missing, they fall back to local rendering.
    • Portal unmounts: Children unmount with the Portal, regardless of whether they were currently teleported or local. The host does not keep orphaned children.
    • Host unmounts: The host disappears, but Portals targeting it survive by falling back to local rendering.
  8. Manage Portal ordering and host name uniqueness

    main

    When using multiple portals, be aware of these two rules:

    Ordering

    • Same commit: If multiple Portals are rendered at the same time (e.g., on initial mount), they appear in the host in the same order they appear in the JSX tree.
    • Different commits: If a Portal mounts after others are already in the host, it is appended to the end of the host's children based on its arrival time, regardless of its position in the source code.

    Host Name Uniqueness

    Host names are matched as strings. Do not mount two PortalHost instances with the same name at the same time. This is undefined behavior; only the most recently registered host will receive new portals, and unmounting one may leave children stranded.

  9. Pre-load heavy components using the Portal pattern

    main

    You can eliminate loading spinners for expensive components (like WebViews, maps, or charts) by rendering them offscreen at app startup and teleporting them into view only when needed.

    The Pattern

    1. At App Startup: Mount the heavy component inside a <Portal> within a hidden, offscreen container (e.g., using position: 'absolute', top: -9999). Assign the Portal a specific hostName.
    2. Background Loading: Because no matching <PortalHost> exists yet, the component renders in its place (offscreen) and initializes (downloads JS, parses, etc.) while the user is on other screens.
    3. On Navigation: When the user navigates to the target screen, mount a <PortalHost> with the matching name.
    4. Instant Rendering: The component teleports into the host. Since react-native-teleport re-parents (moves) the native view rather than re-mounting it, the component appears instantly with its state (scroll position, user input, loaded scripts) fully preserved.
    5. On Unmount: When the <PortalHost> unmounts, the content automatically returns to the offscreen <Portal>.
    // 1. Offscreen Portal at App Root
    <View style={{ position: 'absolute', top: -9999 }}>
      <Portal hostName="my-heavy-component">
        <ExpensiveComponent />
      </Portal>
    </View>
    
    // 2. On the target screen
    <PortalHost name="my-heavy-component" />
  10. Understand Portal and PortalHost static behavior

    main

    The Portal component determines where its children render based on the presence of a matching PortalHost via the hostName prop:

    • No hostName prop: Children render locally in place (transparent wrapper).
    • hostName="x" and a <PortalHost name="x" /> exists: Children are teleported into that host.
    • hostName="x" but no host exists: Children render locally as a fallback. They will automatically migrate to the host as soon as it mounts.

    Note: Because of the fallback rule, a Portal is never a 'black hole'; children are always attached to a live view tree.

    // Renders locally (fallback)
    <Portal hostName="missing-host">
      <Text>I am visible locally</Text>
    </Portal>
    
    // Teleports to host
    <Portal hostName="active-host">
      <Text>I am in the host</Text>
    </Portal>
  11. How Instagram-like shared transitions work with Teleport

    main

    To achieve smooth, uninterrupted transitions (like expanding a video from a feed into a full-screen player), react-native-teleport uses a re-parenting strategy rather than unmounting and remounting components.

    The Workflow

    1. Initial State: The video component lives inside a <Portal> within the source screen (e.g., the Feed).
    2. Transition Start: When a user interacts, the video is teleported to an overlay layer that sits above all screens.
    3. Animation: While in the overlay, the video is animated from its original card size to full-screen dimensions.
    4. Transition End: Once the animation completes, the video is teleported into a <PortalHost> inside the destination screen (e.g., the Reels viewer).
    5. Reversal: Going back reverses this: teleport to overlay $\rightarrow$ animate back to card size $\rightarrow$ return to the original screen.

    Because the component is moved in the native view hierarchy instead of being destroyed and recreated, playback is never interrupted and animations remain fluid.

    Feed (in-place) ──tap──▶ Overlay (animating) ──done──▶ Reels (in-place)
                                                                  │
                             Overlay (animating) ◀──back──────────┘
                                                                │
    Feed (in-place) ◀──done──────┘
  12. When to use the pre-loading pattern

    main

    The pre-loading pattern is most effective when the following conditions are met:

    • Expensive Initialization: The component requires significant time to initialize, such as WebViews loading JS from CDNs, maps, or complex chart libraries.
    • Static Configuration: The component's initial loading/setup does not depend on screen-specific data (it can be pre-loaded with a generic configuration).
    • High Probability of Use: The user is likely to visit the screen. Pre-loading components that are rarely used wastes system resources.

    Note on Content Preservation: Because react-native-teleport re-parents native views rather than re-mounting them, user input (like text typed into a WebView) is preserved when the user navigates away and returns.