npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-sliver

v0.1.2

Published

Sliver-based scroll composition for React Native (inspired by Flutter)

Readme

react-native-sliver 🚀

Sliver-based scroll composition for React Native, inspired by Flutter's powerful Sliver system.

npm version License: MIT TypeScript React Native Expo

🌟 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-reanimated for 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-context

or

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 expandedHeight to collapsedHeight
  • 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: 1000

Key Design Principles:

  1. Context-based State Sharing: All slivers access scroll state via React Context with useSliver() hook
  2. Reanimated Powered: Uses SharedValue for 60fps animations running on the UI thread
  3. Registration System: Slivers can register for offset tracking and coordinate with each other
  4. Composable Architecture: Mix and match slivers freely - order matters!
  5. Special AppBar Handling: SliverAppBar renders outside ScrollView with position: absolute for true persistence

How It Works:

  • SliverScrollView wraps Animated.ScrollView and 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
  • SliverAppBar stays 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

  1. Memoize renderItem callbacks:
const renderItem = useCallback((item, index) => (
  <ItemCard item={item} index={index} />
), []);

<SliverList data={items} renderItem={renderItem} />
  1. Use keyExtractor for lists:
<SliverList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
/>
  1. Extract builder functions for SliverPersistentHeader:
// ✅ Good - defined outside component
const renderHeader = () => <HeaderContent />;

// ❌ Bad - recreated on every render
<SliverPersistentHeader builder={() => <HeaderContent />} />
  1. 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:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. 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 start

Please 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

📞 Support & Contact

💡 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