Shipnative
ShipnativeShipnative
Sign in
GuideSeptember 2026 · 9 min read

React Native Dropdown: Picker, Menu, or Custom

The first surprise moving from the web to React Native is that there is no <select>. There is no dropdown primitive at all. What exists instead is a platform picker that looks nothing like a dropdown on iOS, a handful of community packages, and a very common Android bug that silently cuts your options list in half. This guide covers the four real options, working code for the two most people need, and why your dropdown works in the simulator and breaks on a real Android phone.

The four options

OptionLookExpo GoBest forTrade-off
@react-native-picker/pickerPlatform native✅ YesSettings-style value fieldsiOS renders a 216pt wheel
react-native-element-dropdownStyled by you✅ YesForm fields, search, multi-selectNot a platform control
@react-native-menu/menuTrue native menu❌ Dev buildOverflow and context menusMenus, not form inputs
Custom Modal / sheetFully yours✅ YesLong lists, design-led UIYou own a11y and dismissal

The decision comes down to one question: is this a platform value or a designed field? A unit, a timezone, a repeat interval — those are platform values, and the OS control is the right answer because users already know how it works. A category chip on your add-expense screen is a designed field, and a native wheel will look like it wandered in from a different app.

The platform control: Picker

npx expo install @react-native-picker/picker
import { useState } from 'react';
import { Picker } from '@react-native-picker/picker';

const UNITS = [
  { label: 'Kilograms', value: 'kg' },
  { label: 'Pounds', value: 'lb' },
];

export default function UnitField() {
  const [unit, setUnit] = useState('kg');

  return (
    <Picker
      selectedValue={unit}
      onValueChange={setUnit}
      dropdownIconColor="#8a8a8a"        // Android only
      itemStyle={{ color: '#fff' }}       // iOS only
    >
      {UNITS.map((u) => (
        <Picker.Item key={u.value} label={u.label} value={u.value} />
      ))}
    </Picker>
  );
}

Note the two platform-only props sitting next to each other. That is the honest summary of this component: it is two different widgets behind one name. On Android you get a tappable row that opens a dialog — a real dropdown. On iOS you get an inline spinning wheel that occupies roughly 216 points of vertical space in your layout, whether you wanted it to or not.

Most teams end up wrapping it: render a normal-looking row on iOS, and open the Picker inside a modal or an action sheet when it is tapped. If you are already writing that wrapper, you are most of the way to a custom dropdown, which is why so many projects skip straight to the next option.

The common case: element-dropdown

npm install react-native-element-dropdown

Pure JavaScript, so it works in Expo Go and needs no native rebuild. It renders a field you style yourself and an options list that looks the same on both platforms — which is usually the point.

import { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Dropdown } from 'react-native-element-dropdown';

const CATEGORIES = [
  { label: 'Groceries', value: 'groceries' },
  { label: 'Transport', value: 'transport' },
  { label: 'Rent', value: 'rent' },
  { label: 'Subscriptions', value: 'subs' },
];

export default function CategoryField({ value, onChange }) {
  const [focused, setFocused] = useState(false);

  return (
    <View style={styles.wrap}>
      <Text style={styles.label}>Category</Text>
      <Dropdown
        data={CATEGORIES}
        labelField="label"
        valueField="value"
        value={value}
        onChange={(item) => onChange(item.value)}
        onFocus={() => setFocused(true)}
        onBlur={() => setFocused(false)}
        placeholder="Choose one"
        search
        searchPlaceholder="Search categories"
        style={[styles.field, focused && styles.fieldFocused]}
        containerStyle={styles.menu}
        placeholderStyle={styles.placeholder}
        selectedTextStyle={styles.selected}
        inputSearchStyle={styles.search}
        activeColor="#232323"
      />
    </View>
  );
}

const styles = StyleSheet.create({
  wrap: { marginBottom: 20 },
  label: { color: '#8a8a8a', fontSize: 13, marginBottom: 8 },
  field: {
    height: 52,
    paddingHorizontal: 16,
    borderRadius: 12,
    backgroundColor: '#141414',
    borderWidth: 1,
    borderColor: 'rgba(255,255,255,0.08)',
  },
  fieldFocused: { borderColor: '#fb923c' },
  menu: {
    borderRadius: 12,
    backgroundColor: '#141414',
    borderColor: 'rgba(255,255,255,0.08)',
  },
  placeholder: { color: '#5a5a5a', fontSize: 15 },
  selected: { color: '#fff', fontSize: 15 },
  search: { color: '#fff', borderRadius: 8, borderColor: 'rgba(255,255,255,0.08)' },
});

The search prop is the reason this package wins most comparisons. The moment a list passes roughly fifteen options, scrolling becomes the worst part of the form, and adding search to a hand-rolled dropdown is an afternoon you did not plan for. Multi-select lives in the sibling MultiSelect export with the same API shape.

The Android bug behind most “my dropdown is cut off” reports

The classic hand-rolled dropdown is a field with an absolutely positioned list underneath it and a high zIndex. On iOS that works. On Android it gets clipped at the edge of the nearest parent, because Android does not render children outside their parent bounds and zIndex does not lift a view out of a clipping ancestor the way CSS stacking contexts do.

// ❌ Fine on iOS, cut off on Android inside any card or ScrollView
<View style={{ position: 'relative' }}>
  <Pressable onPress={open}><Text>{value}</Text></Pressable>
  {isOpen && (
    <View style={{ position: 'absolute', top: 52, zIndex: 999, elevation: 10 }}>
      {options.map(renderOption)}
    </View>
  )}
</View>

elevation helps with paint order but not with clipping. The fix is to stop overflowing at all — render the list in a Modal, which mounts outside the view hierarchy and behaves identically on both platforms:

// ✅ Same on both platforms, and dismissal comes for free
<Modal visible={isOpen} transparent animationType="fade" onRequestClose={close}>
  <Pressable style={styles.backdrop} onPress={close} />
  <View style={styles.sheet}>
    <FlatList
      data={options}
      keyExtractor={(o) => o.value}
      renderItem={({ item }) => (
        <Pressable
          onPress={() => { onChange(item.value); close(); }}
          style={styles.row}
          accessibilityRole="button"
          accessibilityState={{ selected: item.value === value }}
        >
          <Text style={styles.rowText}>{item.label}</Text>
        </Pressable>
      )}
    />
  </View>
</Modal>

Two things you now get for free that the absolute-positioned version never had: the hardware back button closes it on Android via onRequestClose, and the list can be taller than the screen without fighting the parent scroll view. The same reasoning is why a bottom sheet is often the better dropdown once you have more than a handful of options — it is the pattern phone users already expect for “pick one of these”.

Three smaller traps

  1. Dropdowns inside a FlatList row.Every open dropdown in a recycled row is a candidate for being unmounted mid-interaction as the list virtualises. Lift the open state to the list’s parent and render a single Modal for the whole screen, keyed by which row asked for it. The FlatList guide covers why row components should stay dumb.
  2. Keyboard still up when the list opens. If the previous field was a text input, the keyboard covers your options. Call Keyboard.dismiss() before opening, or the user taps blind.
  3. No accessible state. A Pressable full of Text is invisible to screen readers as a choice. Set accessibilityRole="button" and accessibilityState={{ selected }} on each option — two attributes, and the field stops being unusable with VoiceOver.

Most dropdowns should not be dropdowns

A dropdown is a desktop control designed for a mouse and a lot of screen. On a phone, four or five options read better as a row of chips or a segmented control — no open state, no dismissal logic, no Android clipping, and one fewer tap. Above roughly fifteen options, a searchable full-screen picker beats a floating list because the user can actually see what they are choosing from. The floating dropdown is genuinely the best answer only in the middle of that range.

If you want to feel the difference before committing, describe the form — “an add-expense screen with amount, a category selector with twelve options, and a date field” — and ShipNative builds it as real React Native running on your phone. Tapping through it for thirty seconds settles the chips-versus-dropdown argument faster than any comparison table.

Frequently Asked Questions

Is there a built-in dropdown in React Native?

No. React Native ships no <select> equivalent in core. The closest official option is @react-native-picker/picker, a community package that wraps the platform picker — a spinning wheel on iOS and a dropdown dialog on Android. Anything that looks like a web select is either a third-party package or a modal you build yourself.

Why is my dropdown cut off on Android but fine on iOS?

Android clips absolutely positioned children that overflow their parent, and zIndex does not lift a view out of a clipping ancestor the way it does on the web. If your dropdown list is a positioned View inside a card, a ScrollView, or a FlatList row, Android will cut it at the parent edge. Render the list inside a Modal instead — that escapes the view hierarchy entirely and behaves identically on both platforms.

How do I make a searchable dropdown in React Native?

react-native-element-dropdown supports it out of the box with the search prop and searchPlaceholder. If you are building your own, a Modal containing a TextInput and a FlatList of filtered options is the standard shape — and once the list is long enough to need search, a full-screen picker screen usually beats a floating dropdown on a phone.

Should I use Picker or a custom dropdown?

Use Picker when the value is a genuine platform choice — a date component, a unit, a country — and you want the OS look. Use a custom Modal or bottom sheet when the dropdown is part of your visual design, because the iOS Picker renders as an inline wheel roughly 216pt tall and cannot be styled into looking like anything else.

Does @react-native-picker/picker work in Expo Go?

Yes. It is included in the Expo Go runtime, so you can install it with expo install and use it without a development build. react-native-element-dropdown is pure JavaScript and also works in Expo Go. @react-native-menu/menu is native and needs a development build.

Can an AI app builder generate dropdowns for me?

Yes. Describe the field — "a category selector on the add-expense screen with about twelve options" — and ShipNative generates the input, the option list, and the state wiring as real React Native, then runs it on your phone so you can see whether a wheel, a sheet, or a full picker screen actually feels right at that size.

→

React Native Form Validation

A dropdown is a form field — this is the rest of the form.

Read guide →
→

React Native Bottom Sheet

The pattern that replaces dropdowns once the option list gets long.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →
ShipNative logoShipnative

Build mobile apps with AI. Describe, preview, and ship to iOS & Android in minutes.

Features

Text to App AIApp Generator from ScreenshotPRD to Mobile App

Tools

All free toolsApp Cost CalculatorApp Name GeneratorApp Store Keyword ToolReact Native Components

Blog

All blog postsHow to Build an App Without CodingBest AI Tools for Real Mobile AppsExpo EAS App Store ChecklistLovable, Cursor & v0 for MobileBest AI App Builders in 2026React Native AI App Builder Guide

Legal

FAQTerms of ServicePrivacy Policy

© 2026 ShipNative. All rights reserved.