react-native-sliver
v0.1.2
Published
Sliver-based scroll composition for React Native (inspired by Flutter)
Maintainers
Readme
react-native-sliver 🚀
Sliver-based scroll composition for React Native, inspired by Flutter's powerful Sliver system.
🌟 Features
- 🎯 Declarative Scroll Composition: Build complex scroll interactions with simple, composable components
- 📱 Flutter-Inspired API: Familiar concepts if you know Flutter, but designed for React Native
- ⚡ Smooth 60fps Animations: Powered by
react-native-reanimatedfor buttery smooth performance - 🎨 Flexible AppBar: Collapsible headers with image scaling, opacity transitions, and sticky behavior
- 📦 Zero Native Code: 100% JavaScript/TypeScript - Fully Expo compatible
- 🔧 Highly Extensible: Easy to create custom slivers for your specific needs
- 🪶 Lightweight & Fast: Minimal dependencies, optimized for performance
- 💪 Production Ready: Battle-tested with TypeScript support and comprehensive documentation
- 🎭 Rich Components: SliverList, SliverGrid, SliverPadding, SliverPersistentHeader, and more
- 🔄 Pull to Refresh: Built-in support out of the box
🎬 Demo
📦 Installation
npm install react-native-sliver react-native-reanimated react-native-safe-area-contextor
yarn add react-native-sliver react-native-reanimated react-native-safe-area-context⚙️ Configure Reanimated
Add the Babel plugin to your babel.config.js:
module.exports = {
presets: [
// ... other presets
],
plugins: [
'react-native-reanimated/plugin', // Must be last
],
};⚠️ Important: The reanimated plugin must be listed last in the plugins array.
🎯 Quick Start
Here's a simple example to get you started:
import {
SliverScrollView,
SliverAppBar,
SliverList,
SliverPadding,
SliverBox,
} from 'react-native-sliver';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];
return (
<SafeAreaProvider>
<SliverScrollView>
<SliverAppBar
title="My Awesome App"
expandedHeight={250}
collapsedHeight={85}
backgroundColor="#FF6B35"
foregroundColor="#FFFFFF"
flexibleSpace={
<Image
source={{ uri: 'https://example.com/header.jpg' }}
style={{ width: '100%', height: '100%' }}
/>
}
/>
<SliverPadding padding={{ horizontal: 16, top: 20 }}>
<SliverBox>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>
Welcome! 👋
</Text>
</SliverBox>
</SliverPadding>
<SliverList
data={items}
renderItem={(item, index) => (
<View style={{ padding: 16, backgroundColor: 'white', margin: 8 }}>
<Text>{item}</Text>
</View>
)}
/>
</SliverScrollView>
</SafeAreaProvider>
);
}🎨 Real-World Example
Check out this complete Bitcoin tracker app showcasing all sliver components in action:
import { StyleSheet, Text, View, Image, TouchableOpacity } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import {
SliverScrollView,
SliverAppBar,
SliverList,
SliverGrid,
SliverPadding,
SliverPersistentHeader,
SliverBox,
} from 'react-native-sliver';
// Helper functions for sticky headers
const renderMarketStatsHeader = () => (
<View style={styles.stickyHeader}>
<Text style={styles.stickyHeaderText}>📊 Market Statistics</Text>
</View>
);
const renderDetailsHeader = () => (
<View style={styles.stickyHeader}>
<Text style={styles.stickyHeaderText}>📈 Bitcoin Details</Text>
</View>
);
const renderFeaturesHeader = () => (
<View style={styles.stickyHeader}>
<Text style={styles.stickyHeaderText}>⚡ Key Features</Text>
</View>
);
const renderTopCryptosHeader = () => (
<View style={styles.stickyHeader}>
<Text style={styles.stickyHeaderText}>🪙 Top Cryptocurrencies</Text>
</View>
);
export default function BitcoinTrackerApp() {
const bitcoinDetails = [
{ icon: '📅', title: 'Launch Date', description: 'January 3, 2009' },
{ icon: '👤', title: 'Creator', description: 'Satoshi Nakamoto' },
{ icon: '🔢', title: 'Max Supply', description: '21 Million BTC' },
// ... more details
];
const keyFeatures = [
{ id: 1, icon: '🔐', title: 'Decentralized', description: 'No central authority' },
{ id: 2, icon: '🛡️', title: 'Secure', description: 'Cryptographic security' },
{ id: 3, icon: '🌍', title: 'Global', description: 'Borderless payments' },
// ... more features
];
const topCryptos = [
{ icon: '₿', name: 'Bitcoin', symbol: 'BTC', price: '$45,234.56', change: '+3.45%' },
{ icon: 'Ξ', name: 'Ethereum', symbol: 'ETH', price: '$2,987.32', change: '+5.12%' },
{ icon: '◎', name: 'Solana', symbol: 'SOL', price: '$104.87', change: '+8.34%' },
// ... more cryptos
];
return (
<SafeAreaProvider>
<View style={styles.container}>
<SliverScrollView>
{/* Collapsible AppBar with Bitcoin Chart Background */}
<SliverAppBar
title="Bitcoin"
expandedHeight={250}
collapsedHeight={85}
backgroundColor="#F7931A"
foregroundColor="#FFFFFF"
flexibleSpace={
<Image
source={{ uri: 'https://images.unsplash.com/photo-1621761191319-c6fb62004040' }}
style={{ width: '100%', height: '100%' }}
resizeMode="cover"
/>
}
/>
{/* Market Statistics Section */}
<SliverPersistentHeader pinned minHeight={50} maxHeight={50} builder={renderMarketStatsHeader} />
<SliverPadding padding={{ horizontal: 16, top: 16, bottom: 8 }}>
<SliverBox>
<View style={styles.statsCard}>
<View style={styles.statRow}>
<Text style={styles.statLabel}>Current Price:</Text>
<Text style={styles.statValue}>$45,234.56</Text>
</View>
<View style={styles.statRow}>
<Text style={styles.statLabel}>24h Change:</Text>
<Text style={[styles.statValue, styles.positive]}>+3.45%</Text>
</View>
<View style={styles.statRow}>
<Text style={styles.statLabel}>Market Cap:</Text>
<Text style={styles.statValue}>$890.2B</Text>
</View>
<View style={styles.statRow}>
<Text style={styles.statLabel}>24h Volume:</Text>
<Text style={styles.statValue}>$35.4B</Text>
</View>
</View>
</SliverBox>
</SliverPadding>
{/* Bitcoin Details List */}
<SliverPersistentHeader pinned minHeight={50} maxHeight={50} builder={renderDetailsHeader} />
<SliverList
data={bitcoinDetails}
renderItem={(item, index) => (
<SliverPadding key={index} padding={{ horizontal: 16, vertical: 4 }}>
<SliverBox>
<View style={styles.detailItem}>
<Text style={styles.detailIcon}>{item.icon}</Text>
<View style={styles.detailContent}>
<Text style={styles.detailTitle}>{item.title}</Text>
<Text style={styles.detailText}>{item.description}</Text>
</View>
</View>
</SliverBox>
</SliverPadding>
)}
/>
{/* Key Features List */}
<SliverPersistentHeader pinned minHeight={50} maxHeight={50} builder={renderFeaturesHeader} />
<SliverList
data={keyFeatures}
renderItem={(item) => (
<SliverPadding key={item.id} padding={{ horizontal: 16, vertical: 8 }}>
<SliverBox>
<View style={styles.featureCard}>
<View style={styles.featureIcon}>
<Text style={styles.featureIconText}>{item.icon}</Text>
</View>
<View style={styles.featureContent}>
<Text style={styles.featureTitle}>{item.title}</Text>
<Text style={styles.featureText}>{item.description}</Text>
</View>
</View>
</SliverBox>
</SliverPadding>
)}
/>
{/* Top Cryptocurrencies Grid */}
<SliverPersistentHeader pinned minHeight={50} maxHeight={50} builder={renderTopCryptosHeader} />
<SliverGrid
data={topCryptos}
gridDelegate={{
type: 'fixed',
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 0.85,
}}
renderItem={(item) => (
<TouchableOpacity style={styles.cryptoCard}>
<View style={styles.cryptoCardIcon}>
<Text style={styles.cryptoCardIconText}>{item.icon}</Text>
</View>
<Text style={styles.cryptoCardName}>{item.name}</Text>
<Text style={styles.cryptoCardSymbol}>{item.symbol}</Text>
<Text style={styles.cryptoCardPrice}>{item.price}</Text>
<Text style={[styles.cryptoCardChange, item.change.startsWith('+') ? styles.positive : styles.negative]}>
{item.change}
</Text>
</TouchableOpacity>
)}
/>
</SliverScrollView>
</View>
</SafeAreaProvider>
);
}📚 API Reference - Complete Component Guide
SliverScrollView
The root container that provides scroll context to all child slivers. It's the foundation of the sliver system.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| onRefresh | () => void | undefined | Callback for pull-to-refresh |
| refreshing | boolean | false | Loading state for refresh |
| bounces | boolean | true | Enable bounce effect (iOS) |
| showsVerticalScrollIndicator | boolean | true | Show scroll indicator |
| contentContainerStyle | ViewStyle | undefined | Style for content container |
| onScroll | (event) => void | undefined | Scroll event callback |
<SliverScrollView
onRefresh={handleRefresh}
refreshing={isRefreshing}
bounces={true}
showsVerticalScrollIndicator={true}
onScroll={(event) => console.log(event.nativeEvent.contentOffset.y)}
>
{/* Your slivers here */}
</SliverScrollView>SliverAppBar
A collapsible app bar with smooth animations for height, image opacity, and title transitions. Perfect for hero headers.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | string | required | Title text displayed in toolbar |
| expandedHeight | number | 200 | Maximum height when expanded |
| collapsedHeight | number | 80 | Minimum height when collapsed |
| backgroundColor | string | '#6200EE' | Background color |
| foregroundColor | string | '#FFFFFF' | Text and icon color |
| flexibleSpace | ReactNode | undefined | Custom content (images, gradients) |
| leading | ReactNode | undefined | Leading widget (back button, menu) |
| titleStyle | TextStyle | undefined | Custom title style |
| style | ViewStyle | undefined | Container style |
| elevation | number | 4 | Shadow elevation (Android) |
Animation Behavior:
- Height: Smoothly interpolates from
expandedHeighttocollapsedHeight - Image: Fades out and scales down as you scroll
- Title: Fades in on the toolbar when collapsed
- Big Title: Fades out at the bottom of expanded header
<SliverAppBar
title="My Profile"
expandedHeight={300}
collapsedHeight={90}
backgroundColor="#1976D2"
foregroundColor="#FFFFFF"
leading={<BackButton />}
flexibleSpace={
<View style={{ width: '100%', height: '100%' }}>
<Image
source={{ uri: 'https://example.com/cover.jpg' }}
style={{ width: '100%', height: '100%' }}
resizeMode="cover"
/>
<View style={styles.overlay}>
<Text style={styles.subtitle}>Welcome back!</Text>
</View>
</View>
}
/>SliverList
Optimized vertical list renderer. Similar to FlatList but integrated with the sliver system.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| data | T[] | required | Array of data |
| renderItem | (item: T, index: number) => ReactNode | required | Item renderer function |
| keyExtractor | (item: T, index: number) => string | auto | Unique key generator |
| ItemSeparatorComponent | ComponentType | undefined | Separator between items |
| ListHeaderComponent | ReactElement | undefined | Header component |
| ListFooterComponent | ReactElement | undefined | Footer component |
| ListEmptyComponent | ReactElement | undefined | Empty state component |
| contentContainerStyle | ViewStyle | undefined | Container style |
| inverted | boolean | false | Reverse list order |
<SliverList
data={messages}
renderItem={(message, index) => (
<MessageBubble
message={message}
isOdd={index % 2 === 0}
/>
)}
keyExtractor={(message) => message.id}
ItemSeparatorComponent={() => <View style={{ height: 8 }} />}
ListHeaderComponent={<ChatHeader />}
ListFooterComponent={<LoadMoreButton />}
ListEmptyComponent={
<EmptyState message="No messages yet" />
}
/>SliverGrid
Flexible grid layout with configurable columns and spacing. Supports fixed column count or adaptive sizing.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| data | T[] | required | Array of data |
| renderItem | (item: T, index: number) => ReactNode | required | Item renderer |
| keyExtractor | (item: T, index: number) => string | auto | Key generator |
| columns | number | 2 | Shorthand for column count |
| gridDelegate | GridDelegate | auto | Advanced grid configuration |
| contentContainerStyle | ViewStyle | undefined | Container style |
<SliverGrid
data={photos}
gridDelegate={{
type: 'fixed',
crossAxisCount: 3,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 1,
}}
renderItem={(photo) => (
<Image source={{ uri: photo.url }} style={styles.photo} />
)}
/>SliverBox
Adapter for arbitrary content. Use this to insert any React component into the sliver flow.
<SliverBox>
<BannerAd />
<FeaturedSection />
</SliverBox>SliverPersistentHeader
A header that can change size during scroll and optionally stick to the top.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| minHeight | number | required | Minimum height when scrolled |
| maxHeight | number | required | Maximum height when at top |
| pinned | boolean | false | Stick to top when scrolled |
| floating | boolean | false | Float back when scrolling up |
| builder | (shrinkOffset: number, overlapsContent: boolean) => ReactNode | required | Content builder function |
| style | ViewStyle | undefined | Container style |
<SliverPersistentHeader
minHeight={60}
maxHeight={150}
pinned={true}
builder={(shrinkOffset, overlapsContent) => {
const progress = shrinkOffset / 90;
return (
<View style={{
height: '100%',
backgroundColor: overlapsContent ? '#FF6B35' : '#FFF',
justifyContent: 'center',
paddingHorizontal: 16,
}}>
<Text style={{ fontSize: 24 - (progress * 8), fontWeight: 'bold' }}>
Dynamic Header
</Text>
</View>
);
}}
/>SliverPadding
Adds padding around child slivers.
<SliverPadding padding={{ horizontal: 20, top: 16, bottom: 32 }}>
<SliverBox>
<ContentCard />
</SliverBox>
</SliverPadding>SliverFillRemaining
Fills the remaining viewport space.
<SliverFillRemaining hasScrollBody={false}>
<View style={styles.footer}>
<Text>You've reached the end!</Text>
</View>
</SliverFillRemaining>useSliver() Hook
Access scroll context from any component inside SliverScrollView.
import { useSliver } from 'react-native-sliver';
import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';
function ParallaxImage({ uri }) {
const { scrollY } = useSliver();
const animatedStyle = useAnimatedStyle(() => {
const translateY = interpolate(scrollY.value, [0, 300], [0, -100]);
return { transform: [{ translateY }] };
});
return <Animated.Image source={{ uri }} style={[styles.image, animatedStyle]} />;
}🎨 Advanced Usage
Pull to Refresh
Add pull-to-refresh functionality to your scroll view:
const [refreshing, setRefreshing] = useState(false);
const onRefresh = async () => {
setRefreshing(true);
try {
await fetchNewData();
} finally {
setRefreshing(false);
}
};
<SliverScrollView onRefresh={onRefresh} refreshing={refreshing}>
{/* Your slivers */}
</SliverScrollView>Custom Animated Sliver with useSliver Hook
Create custom scroll-driven animations:
import { useSliver } from 'react-native-sliver';
import Animated, {
useAnimatedStyle,
interpolate,
Extrapolate,
} from 'react-native-reanimated';
function FadeInSection({ children }) {
const { scrollY } = useSliver();
const animatedStyle = useAnimatedStyle(() => {
const opacity = interpolate(
scrollY.value,
[0, 100, 200],
[0, 0.5, 1],
Extrapolate.CLAMP
);
const translateY = interpolate(
scrollY.value,
[0, 200],
[50, 0],
Extrapolate.CLAMP
);
return {
opacity,
transform: [{ translateY }],
};
});
return (
<Animated.View style={animatedStyle}>
{children}
</Animated.View>
);
}Parallax Background Effect
function ParallaxHeader({ imageUri }) {
const { scrollY } = useSliver();
const imageStyle = useAnimatedStyle(() => {
const translateY = interpolate(
scrollY.value,
[-100, 0, 100, 200],
[-50, 0, 25, 50]
);
const scale = interpolate(
scrollY.value,
[-100, 0],
[1.5, 1],
Extrapolate.CLAMP
);
return {
transform: [
{ translateY },
{ scale },
],
};
});
return (
<SliverBox>
<View style={{ height: 300, overflow: 'hidden' }}>
<Animated.Image
source={{ uri: imageUri }}
style={[{ width: '100%', height: '100%' }, imageStyle]}
resizeMode="cover"
/>
</View>
</SliverBox>
);
}Sticky Tabs with Multiple SliverPersistentHeaders
const TabHeader = ({ title, isActive }) => (
<TouchableOpacity style={[styles.tab, isActive && styles.activeTab]}>
<Text style={styles.tabText}>{title}</Text>
</TouchableOpacity>
);
<SliverPersistentHeader
pinned
minHeight={50}
maxHeight={50}
builder={() => (
<View style={styles.tabBar}>
<TabHeader title="Popular" isActive={activeTab === 'popular'} />
<TabHeader title="Recent" isActive={activeTab === 'recent'} />
<TabHeader title="Trending" isActive={activeTab === 'trending'} />
</View>
)}
/>Combining Multiple Slivers for Complex Layouts
<SliverScrollView>
{/* Hero Section */}
<SliverAppBar
title="My Store"
expandedHeight={280}
collapsedHeight={90}
flexibleSpace={<HeroImage />}
/>
{/* Categories */}
<SliverPadding padding={{ vertical: 16 }}>
<SliverBox>
<CategoryScroller />
</SliverBox>
</SliverPadding>
{/* Featured Products Grid */}
<SliverPersistentHeader
pinned
minHeight={50}
maxHeight={50}
builder={() => <SectionHeader title="Featured" />}
/>
<SliverGrid
data={featuredProducts}
gridDelegate={{
type: 'fixed',
crossAxisCount: 2,
childAspectRatio: 0.75,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
}}
renderItem={(product) => <ProductCard product={product} />}
/>
{/* All Products List */}
<SliverPersistentHeader
pinned
minHeight={50}
maxHeight={50}
builder={() => <SectionHeader title="All Products" />}
/>
<SliverList
data={allProducts}
renderItem={(product) => <ProductListItem product={product} />}
/>
{/* Footer */}
<SliverFillRemaining hasScrollBody={false}>
<StoreFooter />
</SliverFillRemaining>
</SliverScrollView>🏗️ Architecture
react-native-sliver uses a context-based architecture inspired by Flutter's Sliver system:
SliverScrollView (Root Container)
│
├─ SliverContext Provider
│ ├─ scrollY: SharedValue<number>
│ ├─ viewportHeight: SharedValue<number>
│ └─ contentHeight: SharedValue<number>
│
└─ Animated.ScrollView
│
├─ Content (inside ScrollView)
│ ├─ SliverList
│ ├─ SliverGrid
│ ├─ SliverBox
│ ├─ SliverPadding
│ └─ Custom Slivers
│
└─ SliverAppBar (rendered OUTSIDE ScrollView)
└─ position: absolute, zIndex: 1000Key Design Principles:
- Context-based State Sharing: All slivers access scroll state via React Context with
useSliver()hook - Reanimated Powered: Uses
SharedValuefor 60fps animations running on the UI thread - Registration System: Slivers can register for offset tracking and coordinate with each other
- Composable Architecture: Mix and match slivers freely - order matters!
- Special AppBar Handling:
SliverAppBarrenders outside ScrollView withposition: absolutefor true persistence
How It Works:
SliverScrollViewwrapsAnimated.ScrollViewand provides scroll context- Child slivers consume context via
useSliver()hook - Scroll offset (
scrollY) updates on every scroll frame - Each sliver reacts to scroll changes independently
SliverAppBarstays fixed at top using absolute positioning- All animations run on UI thread for maximum performance
🆚 Comparison with Alternatives
| Feature | ScrollView + FlatList | react-native-snap-carousel | react-native-sliver | |---------|----------------------|---------------------------|------------------------| | Multiple lists in one scroll | ❌ Very difficult | ❌ Not supported | ✅ Built-in | | Collapsible headers | ⚠️ Complex manual setup | ❌ Not included | ✅ SliverAppBar | | Sticky section headers | ⚠️ FlatList only | ❌ No | ✅ SliverPersistentHeader | | Grid layouts in scroll | ❌ Nested ScrollViews | ❌ No | ✅ SliverGrid | | Declarative composition | ⚠️ Limited | ⚠️ Limited | ✅ Full control | | Custom scroll animations | ⚠️ Requires Reanimated | ⚠️ Limited | ✅ useSliver() hook | | Performance (60fps) | ✅ Good | ✅ Good | ✅ Excellent | | Expo compatible | ✅ Yes | ✅ Yes | ✅ Yes | | TypeScript support | ✅ Official | ⚠️ Community | ✅ Built-in | | Learning curve | ✅ Easy | ✅ Medium | ⚠️ Medium | | Flutter developers | ❌ Unfamiliar | ❌ Unfamiliar | ✅ Instantly familiar |
Why Choose react-native-sliver?
✅ If you need: Complex scroll compositions with multiple list types
✅ If you want: Flutter-quality scroll interactions in React Native
✅ If you have: Experience with Flutter's sliver system
✅ If you're building: Content-heavy apps (social media, e-commerce, news)
❌ Not ideal for: Super simple apps with one FlatList
❌ Overkill for: Static content that doesn't need advanced scrolling
📖 API Reference
useSliver() Hook
Access scroll context from any child component.
const { scrollY, viewportHeight, contentHeight } = useSliver();Returns:
scrollY: Current scroll offset (SharedValue)viewportHeight: Visible height (SharedValue)contentHeight: Total content height (SharedValue)
💡 Best Practices
Performance Tips
- Memoize renderItem callbacks:
const renderItem = useCallback((item, index) => (
<ItemCard item={item} index={index} />
), []);
<SliverList data={items} renderItem={renderItem} />- Use keyExtractor for lists:
<SliverList
data={items}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>- Extract builder functions for SliverPersistentHeader:
// ✅ Good - defined outside component
const renderHeader = () => <HeaderContent />;
// ❌ Bad - recreated on every render
<SliverPersistentHeader builder={() => <HeaderContent />} />- Optimize images in flexibleSpace:
<SliverAppBar
flexibleSpace={
<Image
source={{ uri: imageUrl }}
resizeMode="cover"
style={styles.headerImage}
// Add these for better performance:
defaultSource={require('./placeholder.png')}
progressiveRenderingEnabled
/>
}
/>Common Patterns
Infinite Scroll with SliverList:
<SliverList
data={items}
renderItem={(item) => <ItemCard item={item} />}
ListFooterComponent={
hasMore ? <ActivityIndicator /> : <EndOfListMessage />
}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
/>Empty State:
<SliverList
data={items}
renderItem={(item) => <ItemCard item={item} />}
ListEmptyComponent={
<View style={styles.emptyState}>
<Text>No items found</Text>
<Button title="Refresh" onPress={refetch} />
</View>
}
/>Search Header:
<SliverPersistentHeader
pinned
minHeight={60}
maxHeight={60}
builder={() => (
<SearchBar
placeholder="Search..."
onChangeText={setSearchQuery}
value={searchQuery}
/>
)}
/>🐛 Troubleshooting
Issue: Animations not smooth
Solution: Make sure react-native-reanimated/plugin is the last plugin in babel.config.js:
plugins: [
// other plugins...
'react-native-reanimated/plugin', // Must be last!
],Then clear cache: yarn start --reset-cache
Issue: SliverAppBar not collapsing
Solution: Ensure expandedHeight is greater than collapsedHeight:
<SliverAppBar
expandedHeight={250} // ✅ Larger
collapsedHeight={85} // ✅ Smaller
/>Issue: White screen on Android
Solution: Add safe area provider:
import { SafeAreaProvider } from 'react-native-safe-area-context';
<SafeAreaProvider>
<SliverScrollView>
{/* content */}
</SliverScrollView>
</SafeAreaProvider>Issue: TypeScript errors with renderItem
Solution: Use correct signature - parameters are NOT destructured:
// ✅ Correct
<SliverList
renderItem={(item, index) => <Card item={item} />}
/>
// ❌ Wrong
<SliverList
renderItem={({ item, index }) => <Card item={item} />}
/>🗺️ Roadmap
- [ ]
SliverAnimatedList- List with item animations - [ ]
SliverReorderableList- Drag to reorder - [ ]
SliverStaggeredGrid- Pinterest-style grid - [ ]
SliverWaterfallFlow- Masonry layout - [ ]
SliverAnimatedBuilder- Scroll-based animation builder - [ ] Web support via react-native-web
- [ ] More Flutter parity (SliverIgnorePointer, SliverOpacity, etc.)
🤝 Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
Development Setup
# Clone the repo
git clone https://github.com/GianSandoval5/react-native-sliver.git
cd react-native-sliver
# Install dependencies
yarn install
# Run the example
cd example
yarn install
yarn startPlease read CONTRIBUTING.md for details on our code of conduct and development process.
📄 License
MIT © GianSandoval5
See LICENSE file for details.
🙏 Acknowledgments
This library is inspired by Flutter's Sliver system - one of the most elegant scroll composition APIs ever designed. Special thanks to the Flutter team for the inspiration and the React Native community for their continuous support.
Credits
- Animations: Powered by react-native-reanimated
- Safe Areas: Handled by react-native-safe-area-context
- Inspiration: Flutter Slivers
📞 Support & Contact
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📧 Email: [email protected]
- 💼 LinkedIn: giansandoval
💡 Open to Collaborations: I'm always excited to collaborate on interesting projects and ideas! Whether it's contributing to open source, building something new together, or discussing React Native development, feel free to reach out. Let's create something amazing! 🚀
🌟 Show Your Support
If this library helped you, please give it a ⭐️ on GitHub!
Made with ❤️, much effort, and dedication for the entire React Native community
Building better scroll experiences, one sliver at a time
