React Native’s core components don’t include gradients — View only accepts a single solid backgroundColor. Add gradients via a community library; two options dominate.
Two libraries — pick by setup
| Library | Use when | Install |
|---|---|---|
expo-linear-gradient | You’re using Expo (managed or bare) | npx expo install expo-linear-gradient |
react-native-linear-gradient | You’re using bare React Native CLI | npm install react-native-linear-gradient + pod install |
If your project has expo-modules-core in dependencies, use the Expo variant — it just works on every platform Expo supports. For bare React Native projects with native modules, use the community lib.
Installation
Expo
That’s it. Expo handles the native linking. Restart your Metro bundler and import:
import { LinearGradient } from 'expo-linear-gradient';
Bare React Native
Then import:
import LinearGradient from 'react-native-linear-gradient';
(Note: bare RN uses default export, Expo uses named export.)
Basic usage
import { LinearGradient } from 'expo-linear-gradient';
import { View, Text, StyleSheet } from 'react-native';
export default function HeroCard() {
return (
<LinearGradient
colors={['#f97316', '#fbbf24']}
style={styles.card}
>
<Text style={styles.title}>Welcome</Text>
</LinearGradient>
);
}
const styles = StyleSheet.create({
card: {
padding: 24,
borderRadius: 16,
},
title: {
color: '#fff',
fontSize: 24,
fontWeight: '700',
},
});
The minimum: pass a colors array (at least 2 entries). The default gradient runs top-to-bottom.
Controlling direction
start and end are objects with x and y (0 to 1):
<LinearGradient
colors={['#f97316', '#fbbf24']}
start={{ x: 0, y: 0 }} // top-left
end={{ x: 1, y: 1 }} // bottom-right (diagonal)
style={styles.card}
/>
Common directions:
| Direction | start | end |
|---|---|---|
| Top → Bottom (default) | {x: 0, y: 0} | {x: 0, y: 1} |
| Left → Right | {x: 0, y: 0} | {x: 1, y: 0} |
| Diagonal (TL → BR) | {x: 0, y: 0} | {x: 1, y: 1} |
| Reverse diagonal (TR → BL) | {x: 1, y: 0} | {x: 0, y: 1} |
Multi-stop gradients
Pass 3+ colors for multi-stop gradients:
<LinearGradient
colors={['#f97316', '#fbbf24', '#fef3c7']}
style={styles.card}
/>
By default the stops are evenly distributed. Override with locations:
<LinearGradient
colors={['#f97316', '#fbbf24', '#fef3c7']}
locations={[0, 0.7, 1]} // first color stops 70%, then quick fade to third
style={styles.card}
/>
locations array must have the same length as colors. Values are 0 to 1.
Common patterns
Card overlay (text on image)
import { ImageBackground } from 'react-native';
<ImageBackground source={{ uri: 'https://example.com/hero.jpg' }} style={styles.bg}>
<LinearGradient
colors={['transparent', 'rgba(0,0,0,0.75)']}
style={StyleSheet.absoluteFillObject}
>
<Text style={styles.title}>Caption over image</Text>
</LinearGradient>
</ImageBackground>
The transparent-to-black gradient ensures the text reads regardless of the image’s content.
Button with hover-style gradient
<TouchableOpacity onPress={handlePress}>
<LinearGradient
colors={['#f97316', '#ea580c']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.button}
>
<Text style={styles.buttonText}>Sign in</Text>
</LinearGradient>
</TouchableOpacity>
Skeleton loading state
<LinearGradient
colors={['#f3f4f6', '#e5e7eb', '#f3f4f6']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={styles.skeleton}
/>
For animated shimmer, combine with react-native-reanimated:
import Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming } from 'react-native-reanimated';
const translate = useSharedValue(0);
useEffect(() => {
translate.value = withRepeat(withTiming(1, { duration: 1500 }), -1);
}, []);
const animStyle = useAnimatedStyle(() => ({
transform: [{ translateX: -200 + translate.value * 400 }],
}));
<View style={styles.skeleton}>
<Animated.View style={[StyleSheet.absoluteFillObject, animStyle]}>
<LinearGradient
colors={['transparent', '#fff', 'transparent']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={StyleSheet.absoluteFillObject}
/>
</Animated.View>
</View>
Status bar overlay (transparent under)
<LinearGradient
colors={['rgba(0,0,0,0.5)', 'transparent']}
style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 80 }}
/>
Performance notes
LinearGradient is a native view — no JS overhead per frame. Animation handled via Reanimated stays at 60fps. The only performance concern is gradient quality on low-end Android devices — gradients can show banding (visible color steps) at large sizes.
Common pitfalls
| Symptom | Cause | Fix |
|---|---|---|
Unable to resolve "expo-linear-gradient" | Forgot to expo install, or wrong import name | Verify install, check Expo vs bare RN naming |
| Gradient is solid color | You passed 1 color in the array (need at least 2) | colors={['#a', '#b']} |
| Direction looks wrong | start and end mixed up | Both are objects with x/y; 0 is start, 1 is end |
| Hard color stop at 50% | locations doesn’t smoothly distribute | Adjust the location values, e.g. [0, 0.7, 1] |
| Banding on Android | Hardware rendering limitation | Add noise overlay or use SVG gradient instead |
Conclusion
React Native gradients are a one-library install + a four-prop component:
colors— array of at least 2 hex/rgba stringsstart/end— direction (default top-to-bottom)locations— fine-tune stop positionsstyle— pass through to the wrapping View
Patterns that cover 95% of needs: text-on-image overlay, gradient buttons, shimmer loading, and status-bar darkening. For everything else, the locations array gives precise control.