react-native-inner-shadow
Version:
react native inner shadows with linear gradient design UI
481 lines (387 loc) • 16.4 kB
Markdown
# react-native-inner-shadow
[English](https://www.npmjs.com/package/react-native-inner-shadow) | [한국어](https://github.com/ShinMini/react-native-inner-shadow/blob/main/docs/README.KR.md)
**react-native-inner-shadow** gives your React Native apps beautiful inset shadows and highlight effects using [React Native Skia](https://shopify.github.io/react-native-skia/docs/getting-started/installation). Create depth in your UI with both solid and gradient backgrounds, plus interactive shadows that respond to touches using [Reanimated](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started).
[](https://www.npmjs.com/package/react-native-inner-shadow)  <a href="https://github.com/ShinMini/react-native-inner-shadow"> <img src="https://img.shields.io/npm/types/typescript" alt="ts-banner" /> </a>
 
<div align="center">
<img width="45%" max-width="450px" alt="Inner shadow & linear shadow sample" src="https://github.com/ShinMini/react-native-inner-shadow/blob/main/docs/imgs/rn-inner-shadow-thubnail.jpg?raw=true" />
<img width="45%" max-width="450px" alt="Inner shadow pressable & toggle sample gif" src="https://github.com/ShinMini/react-native-inner-shadow/blob/main/docs/imgs/rn-inner-shadow-gif.gif?raw=true" />
</div>
## 🔄 What's New in v2.2.0
- **Performance boost**: Optimized rendering for smoother animations and less resource usage
- **Reliable layouts**: Fixed size calculations for consistent component dimensions
- **Better border radius**: Individual corner customization with proper shadow rendering
<details>
<summary>More details</summary>
- Added padding to prevent shadow clipping at edges
- Created `useShadowProperties` hook for cleaner, more consistent shadow handling
- Fixed z-index layering for proper component stacking
- Removed unnecessary wrapper elements for better performance
- Improved shadow rendering across all components
- Enhanced gradient handling for smoother color transitions
</details>
## 📋 Table of Contents
- [react-native-inner-shadow](#react-native-inner-shadow)
- [🔄 What's New in v2.2.0](#-whats-new-in-v220)
- [📋 Table of Contents](#-table-of-contents)
- [🚀 Installation](#-installation)
- [Setup](#setup)
- [🌟 Features](#-features)
- [🧩 Basic Components](#-basic-components)
- [ShadowView](#shadowview)
- [LinearShadowView](#linearshadowview)
- [🔄 Interactive Components](#-interactive-components)
- [ShadowPressable](#shadowpressable)
- [ShadowToggle](#shadowtoggle)
- [🛠 Advanced Usage](#-advanced-usage)
- [Custom Hooks](#custom-hooks)
- [useShadowProperties](#useshadowproperties)
- [useAnimatedOffset](#useanimatedoffset)
- [Border Radius Control](#border-radius-control)
- [Performance Tips](#performance-tips)
- [📚 API Reference](#-api-reference)
- [Constants](#constants)
- [Component Props](#component-props)
- [❓ Troubleshooting](#-troubleshooting)
- [Common Issues](#common-issues)
- [🤝 Contributing](#-contributing)
- [📄 License](#-license)
## 🚀 Installation
```bash
# Using npm
npm install react-native-inner-shadow @shopify/react-native-skia@next react-native-reanimated
# Using Yarn
yarn add react-native-inner-shadow @shopify/react-native-skia@next react-native-reanimated
# Using Expo
npx expo install react-native-inner-shadow @shopify/react-native-skia@next react-native-reanimated
```
### Setup
Add Reanimated to your Babel config:
```js
// babel.config.js
module.exports = {
presets: [
// Your existing presets
],
plugins: [
// Your existing plugins
'react-native-reanimated/plugin',
],
};
```
For iOS, install pods:
```bash
cd ios && pod install && cd ..
```
## 🌟 Features
- **Inset shadows**: Create depth effects not possible with React Native's standard shadows
- **Reflected light**: Add subtle highlights for a more realistic 3D appearance
- **Linear gradients**: Combine shadows with beautiful gradient backgrounds
- **Interactive components**:
- Pressable buttons with tactile shadow animations
- Toggle switches with state-dependent shadow effects
- **Custom styling**:
- Per-corner border radius control
- Precise control over shadow properties
- Animated transitions
- **Performance optimized**:
- Smart layout management
- Minimal re-renders
- Efficient canvas usage
## 🧩 Basic Components
### ShadowView
The foundation component for creating shadows with solid backgrounds:
```tsx
import React from 'react';
import { View, Text } from 'react-native';
import { ShadowView } from 'react-native-inner-shadow';
export default function Example() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ShadowView
inset
backgroundColor="#f0f0f0"
shadowColor="#00000066"
shadowOffset={{ width: 3, height: 3 }}
shadowBlur={5}
style={{
width: 150,
height: 100,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text>Inset Shadow</Text>
</ShadowView>
</View>
);
}
```
### LinearShadowView
For gradient backgrounds with shadows:
```tsx
import React from 'react';
import { View, Text } from 'react-native';
import { LinearShadowView } from 'react-native-inner-shadow';
export default function GradientExample() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<LinearShadowView
inset
from="top"
to="bottom"
colors={['#FF7A7A', '#FFE08C']}
shadowOffset={{ width: 4, height: 4 }}
shadowBlur={8}
style={{
width: 150,
height: 100,
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text style={{ color: 'white' }}>Gradient Shadow</Text>
</LinearShadowView>
</View>
);
}
```
## 🔄 Interactive Components
### ShadowPressable
Create buttons with satisfying press animations:
```tsx
import React from 'react';
import { View, Text } from 'react-native';
import { ShadowPressable } from 'react-native-inner-shadow';
export default function PressableExample() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ShadowPressable
shadowBlur={6}
duration={150}
damping={0.8}
style={{
width: 180,
height: 60,
backgroundColor: '#0081a7',
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
}}
onPress={() => console.log('Pressed!')}
>
<Text style={{ color: 'white', fontWeight: 'bold' }}>Press Me</Text>
</ShadowPressable>
</View>
);
}
```
### ShadowToggle
Toggle components with state-dependent shadows:
```tsx
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { ShadowToggle } from 'react-native-inner-shadow';
export default function ToggleExample() {
const [isActive, setIsActive] = useState(false);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ShadowToggle
isActive={isActive}
activeColor="#E9C46A"
style={{
width: 120,
height: 60,
backgroundColor: '#fefae0',
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
}}
onPress={() => setIsActive((prev) => !prev)}
>
<Text
style={{
color: isActive ? '#515050' : '#888',
fontWeight: 'bold',
}}
>
{isActive ? 'ON' : 'OFF'}
</Text>
</ShadowToggle>
</View>
);
}
```
## 🛠 Advanced Usage
### Custom Hooks
The library provides powerful hooks for advanced customization:
#### useShadowProperties
Centralizes shadow configuration for consistent behavior:
```tsx
import { useShadowProperties } from 'react-native-inner-shadow';
// Inside your component:
const { flatStyle, bgColor, shadowProps, layout, canRenderCanvas, onLayout } =
useShadowProperties({
propWidth,
propHeight,
style,
inset: true,
shadowOffset: { width: 3, height: 3 },
shadowBlur: 5,
propsOnLayout: customOnLayoutHandler,
});
```
#### useAnimatedOffset
Controls pressable animations with fine-grained control:
```tsx
import { useAnimatedOffset } from 'react-native-inner-shadow';
// Inside your component:
const {
onPressIn,
onPressOut,
depth,
offset,
reflectedLightOffset,
inset,
blurRadius,
PressedAnimatedStyle,
} = useAnimatedOffset({
offset: shadowProps.shadowOffset,
reflectedLightOffset: shadowProps.reflectedLightOffset,
blurRadius: shadowProps.shadowBlur,
damping: 0.8,
duration: 150,
onPressIn: customPressInHandler,
onPressOut: customPressOutHandler,
});
```
### Border Radius Control
Customize each corner individually:
```tsx
<ShadowView
style={{
borderTopLeftRadius: 30,
borderTopRightRadius: 10,
borderBottomRightRadius: 30,
borderBottomLeftRadius: 10,
// Other styles
}}
// Other props
>
<Text>Custom Corners</Text>
</ShadowView>
```
### Performance Tips
For best performance:
1. **Set fixed dimensions** whenever possible
2. **Memoize components** using React.memo() to prevent unnecessary re-renders
3. **Use stable keys** when rendering in lists
4. **Cache styles** instead of generating them on each render
```tsx
import React, { memo, useMemo } from 'react';
import { ShadowView } from 'react-native-inner-shadow';
const OptimizedShadowItem = memo(({ title, color }) => {
const styles = useMemo(
() => ({
container: {
width: 150,
height: 100,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
},
}),
[]
);
return (
<ShadowView backgroundColor={color} inset style={styles.container}>
<Text>{title}</Text>
</ShadowView>
);
});
```
## 📚 API Reference
### Constants
The library provides default values in `src/constants.ts`:
| Constant | Value | Description |
| ---------------------------- | ----------- | -------------------------------- |
| CANVAS_PADDING | 50 | Space to prevent shadow clipping |
| BACKGROUND_COLOR | '#FFFFFF' | Default background color |
| SHADOW_OFFSET_SCALE | 2.5 | Default shadow offset scale |
| REFLECTED_LIGHT_OFFSET_SCALE | 2 | Default reflection offset scale |
| SHADOW_BLUR | 2 | Default shadow blur radius |
| REFLECTED_LIGHT_BLUR | 3 | Default reflection blur radius |
| SHADOW_COLOR | '#2F2F2FBC' | Default shadow color |
| REFLECTED_LIGHT_COLOR | '#FFFFFF4D' | Default reflection color |
| DAMPING_DURATION | 150 | Animation duration (ms) |
| DAMPING_RATIO | 0.8 | Animation damping ratio |
### Component Props
<details>
<summary><b>ShadowView Props</b></summary>
| Prop | Type | Default | Description |
| ----------------------- | --------------------------------- | --------------------------- | ---------------------------------------- |
| inset | boolean | false | Makes shadow appear inside the component |
| backgroundColor | string | '#FFFFFF' | Background color |
| shadowColor | string | '#2F2F2FBC' | Shadow color |
| shadowOffset | { width: number, height: number } | { width: 2.5, height: 2.5 } | Shadow position |
| shadowBlur | number | 2 | Shadow blur radius |
| reflectedLightColor | string | '#FFFFFF4D' | Highlight color |
| reflectedLightOffset | { width: number, height: number } | Auto-calculated | Highlight position |
| reflectedLightBlur | number | 3 | Highlight blur radius |
| isReflectedLightEnabled | boolean | true | Whether to show highlights |
| style | ViewStyle | - | React Native style object |
| children | ReactNode | - | Component children |
</details>
<details>
<summary><b>LinearShadowView Props</b> (extends ShadowView Props)</summary>
| Prop | Type | Default | Description |
| ------ | -------------------------------------- | -------- | ------------------------ |
| from | 'top' \| 'bottom' \| 'left' \| 'right' | 'top' | Gradient start direction |
| to | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' | Gradient end direction |
| colors | Color[] | - | Array of gradient colors |
</details>
<details>
<summary><b>ShadowPressable Props</b></summary>
| Prop | Type | Default | Description |
| ----------------------- | ------- | ------- | ---------------------------------- |
| duration | number | 150 | Animation duration (ms) |
| damping | number | 0.8 | How deeply shadows indent on press |
| isReflectedLightEnabled | boolean | true | Whether to show highlights |
| ...ShadowView Props | - | - | All ShadowView props are supported |
| ...PressableProps | - | - | All React Native Pressable props |
</details>
<details>
<summary><b>ShadowToggle Props</b></summary>
| Prop | Type | Default | Description |
| ------------------------ | ------- | ------- | ---------------------------- |
| isActive | boolean | false | Current toggle state |
| activeColor | string | - | Background color when active |
| ...ShadowPressable Props | - | - | All ShadowPressable props |
</details>
## ❓ Troubleshooting
### Common Issues
1. **Shadows Not Showing**
- Make sure width and height are defined (either in style or as props)
- Check border radius values are reasonable for your component size
- Verify shadow colors have opacity (e.g., '#00000066' not '#000000')
2. **Dependency Errors**
- Ensure all three dependencies are properly installed
- Check your babel.config.js includes 'react-native-reanimated/plugin'
- For iOS, run pod install after installation
- For Expo, make sure you're using compatible versions of all packages
3. **Performance Problems**
- Specify fixed dimensions when possible
- Use React.memo() for components in lists
- Check if you're creating new styles on each render
- For scrolling lists, consider virtualizing your list
4. **Gradient Not Working**
- Verify your colors array has at least 2 colors
- Check from/to directions are valid ('top', 'bottom', 'left', 'right')
## 🤝 Contributing
Contributions welcome! Check out our [Contributing Guide](https://github.com/ShinMini/react-native-inner-shadow/blob/main/docs/CONTRIBUTING.md) to get started.
## 📄 License
This project is [ISC licensed](https://github.com/ShinMini/react-native-inner-shadow/blob/main/LICENSE).
---
Built by [ShinMini](https://github.com/ShinMini) with ❤️