React Native

React Native Shadow Styles Not Working on Android

Shadow styles that render correctly on iOS but show nothing at all on Android is expected platform behavior, not a bug — iOS and Android use fundamentally different shadow rendering systems, and React Native's iOS shadow properties simply have no effect on Android at all without an additional, Android-specific property.

The Problem

A component styled with standard shadow properties renders a visible drop shadow on iOS, but the exact same style produces no visible shadow whatsoever on Android:

const styles = StyleSheet.create({
  card: {
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
  },
});

Why It Happens

React Native's shadowColor, shadowOffset, shadowOpacity, and shadowRadius properties map directly to iOS's native shadow rendering (CALayer shadow properties under the hood), which has no equivalent implementation on Android's rendering system. Android instead uses a property called elevation, which produces a shadow based on a simulated Z-axis depth, following Android's own Material Design shadow conventions — a completely different rendering approach that the iOS-style shadow properties don't translate to automatically.

The Fix

Add the elevation property for Android alongside the existing iOS shadow properties, since neither platform's property has any effect on the other platform:

const styles = StyleSheet.create({
  card: {
    // iOS
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    // Android
    elevation: 5,
  },
});

Both sets of properties can coexist safely in the same style object — each platform simply ignores the properties meant for the other, so this isn't conditional logic, just including both.

Be aware that elevation's visual result differs meaningfully from the iOS shadow properties — it doesn't support the same fine-grained control over shadow color, exact offset, or blur radius that iOS's properties do. Higher elevation values produce a larger, more diffuse shadow, roughly analogous to increasing shadowRadius, but the two won't produce visually identical results across platforms even with values chosen to roughly match.

If you need the shadow's color to be something other than Android's default (typically black-based), note that elevation alone doesn't support custom shadow color on Android versions before Android 9 (API 28) — for consistent custom-colored shadows across a wider range of Android versions, a third-party library specifically designed for cross-platform shadow rendering (several exist in the React Native ecosystem) provides more consistent control than the built-in properties alone.

For a component with a background color and rounded corners, make sure overflow: 'hidden' isn't set on the same style, since this clips the shadow before it can render — a very common conflict when developers add overflow: 'hidden' to enforce rounded corners on child content, unintentionally clipping the parent's own shadow at the same time:

// If you need both rounded corners AND a shadow, apply overflow:hidden
// to an inner wrapping View, not the same View that has the shadow/elevation
<View style={styles.shadowWrapper}>
  <View style={{ overflow: 'hidden', borderRadius: 8 }}>
    {/* content that needs clipping */}
  </View>
</View>

Still Not Working?

If elevation is set but still not producing a visible shadow on Android, check whether the component has a background color explicitly set. In some cases, particularly with certain layout configurations, Android's shadow rendering doesn't display correctly on a fully transparent background — explicitly setting backgroundColor on the shadowed element (even to white, if that's the intended visual result) often resolves an otherwise invisible elevation shadow:

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#fff', // often required for elevation to render visibly
    elevation: 5,
  },
});

It's also worth checking whether a parent container has overflow: 'hidden' set somewhere further up the component tree, not just on the immediate parent — since Android's elevation shadow extends visually beyond the component's own bounding box, an ancestor clipping its own content can cut off a child's shadow even when neither the shadowed component nor its direct parent has any obvious clipping style applied.

Finally, if you're testing on an emulator specifically, confirm the emulator's own rendering isn't the limiting factor — some older emulator images or specific Android API level emulators have historically had inconsistent or degraded shadow rendering compared to physical devices. Testing the same build on a real Android device, or a more recent emulator image, rules this out as a testing-environment artifact rather than a genuine code issue before spending more time debugging styles that may already be correct.