npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-epic-shared-element

v0.2.1

Published

Native shared-element measurements and Reanimated transitions for React Native.

Readme

React Native Epic Shared Element

A native, lightweight, and customizable shared-element transition library for React Native. It measures views on iOS and Android, then animates their geometry with react-native-reanimated.


✨ Features

  • 🎯 Native frame measurement on iOS and Android
  • 🎯 Smooth size and position interpolation between two elements
  • 🎯 Composable geometry and trajectory presets
  • 🎯 Built-in presets: linear, spiral, slingshot, arc, swoosh, and portalWarp
  • 🎯 Custom transition presets with worklet support
  • 🎯 Optional border-radius interpolation
  • 🎯 Custom transition elements
  • 🎯 Configurable clipping and frame tracking
  • 🎯 TypeScript support out of the box
  • 🎯 Compatible with React Native New Architecture

🎥 Demo

The repository includes a complete example gallery in example/src/App.tsx. It demonstrates multiple transition presets, native measurements, custom radii, and transitions between different element sizes.

To run the example:

yarn install
yarn example ios

or

yarn example android

📦 Installation

npm install react-native-epic-shared-element react-native-reanimated react-native-worklets

or

yarn add react-native-epic-shared-element react-native-reanimated react-native-worklets

Note: Make sure react-native-reanimated and react-native-worklets are configured according to their installation guides.


🚀 Basic Usage

import { useCallback, useState } from 'react';
import { Button, Image } from 'react-native';
import {
  SharedElement,
  SharedElementHost,
  SharedElementProvider,
  SharedElementTransitionLayer,
  SharedText,
  Geometry,
  Projection,
  mix,
} from 'react-native-epic-shared-element';
import {
  ReduceMotion,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import { scheduleOnRN } from 'react-native-worklets';

const artwork = require('./artwork.png');

export function Screen() {
  const progress = useSharedValue(0);
  const [transitionActive, setTransitionActive] = useState(false);

  const open = useCallback(() => {
    setTransitionActive(true);
    progress.value = withTiming(
      1,
      { duration: 620, reduceMotion: ReduceMotion.Never },
      (finished) => {
        if (finished) scheduleOnRN(setTransitionActive, false);
      }
    );
  }, [progress]);

  return (
    <SharedElementProvider>
      <SharedElementHost style={{ flex: 1 }}>
        <SharedElementTransitionLayer
          active={transitionActive}
          transitions={[
            {
              key: 'artwork',
              startId: 'artwork-small',
              endId: 'artwork-large',
              progress,
              transition: mix(Geometry.resize, Projection.linear),
            },
          ]}
        >
          <Button title="Open artwork" onPress={open} />

          <SharedElement id="artwork-small" borderRadius={24}>
            <Image source={artwork} style={{ width: 120, height: 120 }} />
          </SharedElement>

          <SharedElement id="artwork-large" borderRadius={8}>
            <Image source={artwork} style={{ width: 320, height: 420 }} />
          </SharedElement>
        </SharedElementTransitionLayer>
      </SharedElementHost>
    </SharedElementProvider>
  );
}

progress is a Reanimated SharedValue<number> in the range 0..1. The SharedElementHost defines the coordinate space used by native measurements.

The component reads progress; the application owns the animation that updates it. Use reduceMotion: ReduceMotion.Never on that animation when the shared element transition must remain animated while the device's reduced-motion setting is enabled.

SharedElementTransitionLayer mounts transition copies above its children only while active is true. Set it to true before animating away from either endpoint, then set it to false after progress reaches 0 or 1. This keeps completed transition copies out of the view tree and supports multiple simultaneous shared elements through the transitions array. SharedElementTransition remains available when the consumer needs to manage placement and mounting directly.

Every SharedElement must be inside a SharedElementHost. Native measurements are layout coordinates relative to that host: ancestor translations/scales used for presentation are excluded, while scroll offsets inside the host are included. The host is kept in the native view hierarchy on both Fabric and Paper. Until its native ref is ready, no measurement is emitted; there is no fallback to window coordinates. Render the transition overlay at the host's origin. If elements use different hosts, their origins and coordinate units must be aligned by the caller.

onFrameChange reports only delivered geometry changes. Its event contains the previous and current rect plus framesDiff, the number of sampled native display frames since the previous delivered change. onFrameSettled fires once per native view, when its initial rect remains unchanged for two consecutive display frames. By default, measurement continues after the initial settle so scrolling and other ancestor layout changes keep the rect current. Set trackFrame={false} to stop continuous measurement; it resumes for one sample after a later element layout.

useSharedElementRegistry().waitForStableRects(ids, callback) remains available to coordinate the initial settle of several elements. It returns a cancellation function and does not drive animations or navigation.

Text content

SharedText is a shared-element variant of React Native's Text. It measures the widest rendered line and the rendered text height through onTextLayout, so it can be used directly instead of wrapping a text component in SharedElement:

<SharedText id="title-small" style={styles.title}>
  A short title
</SharedText>

<SharedText id="title-large" style={styles.titleLarge}>
  A much longer title
</SharedText>

<SharedElementTransition
  startId="title-small"
  endId="title-large"
  progress={progress}
  transition={mix(Geometry.zoom, Projection.linear)}
/>

🎨 Transition Presets

Built-in presets are available from Projection:

<SharedElementTransition
  startId="artwork-small"
  endId="artwork-large"
  progress={progress}
  transition={mix(Geometry.resize, Projection.spiral)}
/>

Available presets:

| Preset | Description | | :----------- | :---------------------------------------------- | | linear | Direct interpolation from source to destination | | spiral | Curved movement with rotation | | slingshot | Accelerated movement with an overshooting arc | | arc | Arc movement with subtle scale and rotation | | swoosh | Bézier swoosh path | | portalWarp | Bézier path with scale and opacity distortion |

Custom preset

Custom presets are worklet callbacks. The transition controls the element size; your preset can customize position, transforms, and opacity:

const customPreset = ({ progress: t, start, end }) => {
  'worklet';

  return {
    left: start.x + (end.x - start.x) * t,
    top: start.y + (end.y - start.y) * t - Math.sin(t * Math.PI) * 24,
    transform: [{ rotate: `${t * Math.PI}rad` }],
  };
};

<SharedElementTransition
  startId="artwork-small"
  endId="artwork-large"
  progress={progress}
  transition={mix(Geometry.resize, customPreset)}
/>;

⚙️ API

SharedElement

| Prop | Type | Default | Description | | :------------- | :-------- | :------ | :--------------------------------------------------- | | id | string | — | Unique identifier used by the transition | | borderRadius | number | — | Native border radius, interpolated during transition | | trackFrame | boolean | true | Continuously measure a moving element | | throttle | number | 16 | Frame measurement interval in milliseconds |

SharedElementTransition

| Prop | Type | Default | Description | | :----------- | :------------------------------ | :------- | :-------------------------------------------------- | | startId | string | — | Source shared-element ID | | endId | string | — | Destination shared-element ID | | progress | SharedValue<number> | — | Transition progress from 0 to 1 | | transition | SharedElementTransitionConfig | resize + linear | Geometry and projection preset | | clip | boolean | true | Clips the transition element to its animated bounds | | element | ReactElement | — | Custom element rendered during the transition |

Position and borderRadius are interpolated from source to destination by default. Size remains at the source dimensions unless a geometry preset is used. Compose geometry and trajectory independently:

transition={mix(Geometry.resize, Projection.arc)}

Geometry presets are resize, zoom, aspectResizeWidth, and aspectResizeHeight; trajectory presets are linear, spiral, slingshot, arc, swoosh, and portalWarp.

You can also pass the transition element as a child:

<SharedElementTransition
  startId="artwork-small"
  endId="artwork-large"
  progress={progress}
  element={<Image source={artwork} />}
/>

SharedElementTransitionLayer

| Prop | Type | Default | Description | | :------------ | :--------------------------------------------- | :------ | :----------------------------------------------- | | active | boolean | — | Mounts transition copies while an animation runs | | transitions | readonly SharedElementTransitionDescriptor[] | — | Transition props plus a unique React key | | style | StyleProp<ViewStyle> | — | Optional style for the absolute overlay | | children | ReactNode | — | Content rendered below the transition overlay |

Keep the layer inside the same SharedElementHost as both endpoints. The layer uses that host's coordinate space and renders its overlay after children, so transition copies appear above the presented content.


🛠 Requirements

  • React Native >= 0.71
  • react-native-reanimated >= 3.16
  • react-native-worklets >= 0.5
  • iOS and Android

🤝 Contributing

We welcome contributions! Please read the Contributing Guide to learn how to help improve Epic Shared Element.


📄 License

MIT License © 2024 Oleh Mazur


Built with ❤️ using create-react-native-library