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

paginated-search-dropdown

v1.0.1

Published

A highly customizable, paginated async search dropdown for React Native (Expo and Bare).

Readme

Paginated Search Dropdown

A highly customizable, paginated async search dropdown for React Native (Expo and Bare).

🎬 Demo

Features

  • Async Search - Load options dynamically based on search query
  • Pagination - Built-in infinite scroll support for loading more results
  • Debounce - Configurable debounce to prevent excessive API calls
  • Android Touch & Scroll Perfected - Native flow-based rendering on Android to prevent touch-clipping and parent ScrollView scroll-gesture conflicts
  • Customizable - Full control over styling and rendering
  • Imperative Controls - Focus, blur, clear, and set search text via ref
  • TypeScript - Full TypeScript support with generic types

Installation

npm install paginated-search-dropdown

or

yarn add paginated-search-dropdown

Usage

import { PaginatedSearchDropdown } from 'paginated-search-dropdown';

interface User {
  id: string;
  name: string;
  email: string;
}

const loadOptions = async (search: string, prevOptions: User[]): Promise<{
  options: User[];
  hasMore: boolean;
}> => {
  const response = await fetch(
    `https://api.example.com/users?q=${search}&offset=${prevOptions.length}`
  );
  const data = await response.json();
  return {
    options: data.users,
    hasMore: data.hasMore,
  };
};

const renderItem = (item: User) => (
  <View>
    <Text>{item.name}</Text>
    <Text style={{ fontSize: 12, color: '#666' }}>{item.email}</Text>
  </View>
);

export default function App() {
  const handleSelect = (user: User) => {
    console.log('Selected:', user);
  };

  return (
    <PaginatedSearchDropdown
      loadOptions={loadOptions}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      onSelect={handleSelect}
      placeholder="Search users..."
    />
  );
}

API

PaginatedSearchDropdown Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | loadOptions | LoadOptions<T, Additional> | Required | Async function to load options | | renderItem | (item: T, index: number) => ReactElement | Required | Render function for each item | | keyExtractor | (item: T, index: number) => string | Required | Key extractor for FlatList | | onSelect | (item: T) => void | Required | Callback when item is selected | | placeholder | string | 'Search...' | Placeholder text | | debounceTimeout | number | 300 | Debounce delay in milliseconds | | initialAdditional | Additional | undefined | Initial additional data | | containerStyle | ViewStyle | undefined | Container styles | | inputStyle | ViewStyle | undefined | Input field styles | | menuStyle | ViewStyle | undefined | Dropdown menu styles | | itemContainerStyle | ViewStyle | undefined | Item container styles | | renderNoResults | () => ReactElement | undefined | Custom no results component | | renderLoading | () => ReactElement | undefined | Custom loading component | | renderFooter | () => ReactElement | undefined | Custom footer component | | renderHeader | () => ReactElement | undefined | Custom header component | | closeOnSelect | boolean | true | Close dropdown on selection | | statusBarTranslucent | boolean | false | Adjusts menu spacing under status bar | | onFocus | () => void | undefined | Callback when search input is focused | | onBlur | () => void | undefined | Callback when search input is blurred |

PaginatedSearchDropdownHandle

Imperative methods available via ref:

const ref = useRef<PaginatedSearchDropdownHandle>(null);

ref.current?.focus();      // Focus the input
ref.current?.blur();       // Blur the input
ref.current?.clear();      // Clear the search text
ref.current?.setSearch('custom text'); // Set custom search text

LoadOptions Function

type LoadOptions<T, Additional = unknown> = (
  search: string,           // Current search text
  prevOptions: T[],         // Previously loaded options
  additional?: Additional   // Additional data from previous page
) => Promise<{
  options: T[];      // New options to add
  hasMore: boolean;  // Whether more pages are available
  additional?: Additional; // Additional data to pass to next page
}>;

Hook Usage

You can also use the usePaginatedSearch hook directly for more control:

import { usePaginatedSearch } from 'paginated-search-dropdown';

const MyComponent = () => {
  const {
    search,
    setSearch,
    options,
    isLoading,
    isLoadingMore,
    hasMore,
    loadMore,
  } = usePaginatedSearch({
    loadOptions: myLoadFunction,
    debounceTimeout: 300,
  });

  // Custom implementation...
};

Requirements

  • React >= 16.8.0
  • React Native >= 0.60.0 (works with Expo and Bare workflows)

License

MIT