# react-native-fast-shadow: real Android shadows without the memory bill

> react-native-fast-shadow is a React Native component that renders genuine blurred shadows on Android by generating reusable NinePatch drawables, and a shadowStyle helper that keeps the look consistent with iOS and the web. It suits an app that needs designed shadows on many views, and it does not support freeform shapes or a blur radius above 25.

**alan-eu/react-native-fast-shadow** — Fast and high quality Android shadows for React Native

- Repository: https://github.com/alan-eu/react-native-fast-shadow
- Stars: 596 · Forks: 12
- Language: Java
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/alan-eu-react-native-fast-shadow

## Why elevation is not enough for designed shadows

React Native only supports shadows on Android through the elevation prop, and the documentation's complaint is specific: achieving the desired effect is often impossible, because it only comes with very few presets.

That is the gap this package is built for. The elevation prop is a discrete, platform-defined scale, and a design that specifies a 12 pixel radius, a particular offset and a 40 percent opacity has no elevation value that reproduces it. So the developer either compromises the design or reaches for a third party library.

And that is where the second problem appears. Third party libraries have been created to circumvent this, but when used on many views they can make an app slower or significantly increase its memory consumption. A shadow library that allocates a large bitmap per view is a memory leak with a nice API.

The project therefore positions itself on two axes at once. It provides the design freedom that elevation denies, and it tries to provide it without the cost that other approaches charge. The four features it lists make that trade explicit: easy to use as a drop-in replacement for the View component, performant with no significant performance impact when applied to a large number of views, customizable with all the regular shadow props, and universal in that it works on all platforms.

The universal part has a specific meaning. On iOS and on the web, ShadowedView is just an alias of View, so the same code runs everywhere and you get the platform's own shadow behaviour there.

## The component API and the shadowStyle helper

Installation is a normal npm install:

```sh
npm install react-native-fast-shadow
# or
yarn add react-native-fast-shadow
```

The usage is a wrapper component with the shadow props in the style object:

```jsx
import { ShadowedView } from 'react-native-fast-shadow';

<ShadowedView
  style={{
    shadowOpacity: 0.4,
    shadowRadius: 12,
    shadowOffset: {
      width: 5,
      height: 3,
    },
  }}
>
  <Image source={require('./kitten.png')} style={{ borderRadius: 30 }} />
</ShadowedView>
```

The supported props are the regular shadow ones, shadowRadius, shadowColor, shadowOpacity and shadowOffset, which means a designer can hand over the values and a developer can paste them without a translation table.

The second export, shadowStyle, exists to solve a different problem, and its explanation is the most useful sentence in the documentation. It creates the same style prop, but divides the shadow radius by 2 on iOS, for the reason that iOS shadows are too large by a factor of 2 when compared to design tools or to CSS box-shadows. The same shadow in a design file, in CSS and on iOS has three different sizes, and the helper makes the numbers line up:

```jsx
import { ShadowedView, shadowStyle } from 'react-native-fast-shadow';

<ShadowedView
  style={shadowStyle({
    opacity: 0.4,
    radius: 12,
    offset: [5, 3],
  })}
>
  <Image source={require('./kitten.png')} style={{ borderRadius: 30 }} />
</ShadowedView>
```

Note the different shape of the input, opacity, radius and a two element offset array, rather than the React Native style keys. The helper is a design-token translator, and that is arguably more useful than the shadow itself.

## How the shadow drawable is actually produced

The under the hood section describes a four step generation process, implemented in ShadowFactory.java under the android source directory.

First, the shape of the view, a rectangle with rounded corners, is painted in black on a canvas. Second, a gaussian blur is applied to that canvas with the given shadowRadius, using the Renderscript API. Third, the result is converted into a NinePatchDrawable, and the reason is specific: it ensures that the corners of the shadow are not distorted when the view is resized. Because of that, only a small shadow drawable has to be generated, and it can be reused for all views with the same border and blur radii. Finally, the drawable is rendered behind the view content, tinted with shadowColor and shadowOpacity, and offset according to shadowOffset.

The reuse step is the whole performance argument, and it is worth being precise about what is reused. It is not a bitmap of the shadow at a particular size, it is a NinePatch, which is a nine-slice image with stretchable regions. That is what allows a single small drawable to serve views of different sizes without distorting the corners, and it is why the memory cost of a shadow does not scale with the number of views the way it does with libraries that rasterise per view.

One dependency to note is that the blur goes through the Renderscript API. Renderscript is a legacy Android graphics API, so on current Android versions the rendering path is worth verifying on the devices you actually ship to.

## The four limitations the documentation states outright

The troubleshooting section is unusually candid, and each of the four items is a design consequence rather than a bug.

The first is that it only works with rounded rectangles. Unlike the iOS View implementation, ShadowedView will not work with freeform views, and it expects its direct descendant to be a rounded rectangle, up to a circle. For shadowed text elements the documentation points at textShadowRadius instead, and for complex shapes it points at react-native-androw.

The second is that ShadowedView expects its child view to fill it. You have to make sure ShadowedView and its children have the same size, otherwise the shadow is larger than the content, and the mental model offered is that ShadowedView is a view with a background colour.

The third is that corner radii can be inferred incorrectly. The radius is taken from the style of ShadowedView or of its direct child, so in a more complex view hierarchy the inference can go wrong, and the fix is to rework the hierarchy or to pass borderRadius directly to the style prop of ShadowedView.

The fourth is that the shadow radius is limited to 25 or below, and the reason is the Renderscript blur effect. The suggested workaround is to downscale the shadow bitmap before applying the blur, and the project asks readers to say so if that limit matters to them.

All four are predictable if you know them before you start, and each of them is a real constraint on a design system that uses shadows heavily.

## The memory comparison is the reason to look at this at all

The benchmark section compares the memory consumption of react-native-fast-shadow with react-native-shadow-2 and react-native-androw, rendering 100 images of 150 by 200 points with a 12 point radius shadow on a Pixel 2, with the app built using the debug variant and Hermes.

The figures in the table are the project's own, and they are the argument for the whole design. With no shadow at all the reference is 117MB. react-native-shadow-2 reaches 430MB, an increase of 313MB, and react-native-androw reaches 403MB, an increase of 286MB. react-native-fast-shadow sits at 123MB, an increase of 6MB.

Read that as an order of magnitude, not as a precise measurement. It is a debug build, which is not what ships, and it is one device and one scene. But the shape of the result is what the architecture predicts: a per view rasterised blur costs memory per view, while a shared NinePatch costs a small drawable that is reused.

For an application that puts a card shadow on every row of a long list, the difference between six megabytes and three hundred is the difference between a list that scrolls and one that does not.

## Where this is the wrong choice, and what the alternatives cost

Three situations point elsewhere, and each alternative has a cost of its own.

The first is a freeform shadow. The documentation does not pretend to solve this, it names another library for it, react-native-androw, which the benchmark shows is the memory hungry option at 403MB in the same test. So a design with irregular shadows is a case where you either accept that library's cost or you change the design.

The second is a single shadow on a single card. The elevation prop handles that case for free, and the whole mechanism of this library, the Renderscript blur and the NinePatch conversion, is worth nothing if there is one shadow in the app.

The third is a cross platform design system where you want identical rendering everywhere. Here the platform's own shadow implementation is arguably better than a cross platform approximation, and shadowStyle only papers over the size difference on iOS, it does not reproduce the same pixels.

The alternative approach in general is to stop asking the platform for a shadow at all and draw the effect in the design system, using a background gradient, a border, or a layered view. That gives full control and identical results on every platform, at the cost of being more work in the design file and less automatic behaviour.

On maintenance, the picture is modest. The package version is 0.1.1, with v0.1.0 from 2022-10-25 and v0.1.1 from 2024-08-05, and the repository is not archived with the last push on 2026-07-16. The licence is MIT, and the dev dependencies target React 17 and React Native 0.68, so the example application is built against an older React Native than a new project would use.

## Conclusion

Adopt react-native-fast-shadow if your Android app needs shadows that the elevation presets cannot express, and you are applying them to a lot of views where memory matters. Do not adopt it for freeform or irregular shapes, for a blur radius above 25, or if you only need one card shadow where the built in elevation prop is enough. Verify first that your views are rounded rectangles whose children fill them, that the corner radius inference works for your hierarchy or that you pass borderRadius explicitly, and that the rendering path still behaves on the Android versions you ship to.

## FAQ

### How do I install and use react-native-fast-shadow?

Run npm install react-native-fast-shadow, then import ShadowedView and place shadowOpacity, shadowRadius, shadowOffset and shadowColor in its style prop. On iOS and web the component is an alias of View, so the same code runs on every platform.

### What is the shadowStyle helper for in react-native-fast-shadow?

It creates the style prop with the shadow radius divided by 2 on iOS, because iOS shadows are too large by a factor of 2 compared to design tools or CSS box-shadows. Its input is opacity, radius and a two element offset array, so it doubles as a design token translator.

### What are the limitations of react-native-fast-shadow?

It only works with rounded rectangles, it expects the child view to fill the ShadowedView or the shadow will be larger than the content, corner radii can be inferred incorrectly in complex hierarchies, and the shadow radius is limited to 25 or below because of the Renderscript blur effect.

### How does react-native-fast-shadow render the shadow on Android?

The view shape is painted in black on a canvas, blurred with the given radius using the Renderscript API, converted to a NinePatchDrawable so corners survive resizing, and then drawn behind the content, tinted with shadowColor and shadowOpacity and offset by shadowOffset. The small drawable is reused for all views with the same border and blur radii.

### What is the memory difference the project reports for react-native-fast-shadow?

Rendering 100 images of 150 by 200 points with a 12 point radius shadow on a Pixel 2, in a debug build with Hermes, the project reports 117MB with no shadow, 430MB for react-native-shadow-2, 403MB for react-native-androw and 123MB for react-native-fast-shadow.

## Sources

- [alan-eu/react-native-fast-shadow on GitHub](https://github.com/alan-eu/react-native-fast-shadow)
- [Issues](https://github.com/alan-eu/react-native-fast-shadow/issues)
- [License: MIT](https://github.com/alan-eu/react-native-fast-shadow/blob/main/LICENSE)
- [README](https://github.com/alan-eu/react-native-fast-shadow/blob/main/README.md)
- [Releases](https://github.com/alan-eu/react-native-fast-shadow/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/alan-eu-react-native-fast-shadow
