Contributing
The repo is a pnpm workspace: the library at the root, an Expo example app in example/ that renders against the library source (edits hot-reload), and this docs site in website/.
Setup
git clone https://github.com/elik56a/react-native-pickers.git
cd react-native-pickers
pnpm install # also installs the git hooks
Scripts
| Command | What it does |
|---|---|
pnpm web | Example app in the browser |
pnpm ios | Example app in the iOS Simulator |
pnpm android | Example app in the Android emulator |
pnpm docs | This site at localhost:3001 |
pnpm test | Jest + React Native Testing Library |
pnpm lint | Biome: lint, format and import order |
pnpm format | Fix whatever lint can fix |
pnpm typecheck | TypeScript, including tests |
pnpm build | Compile to lib/ (ESM + .d.ts) |
pnpm gen:emoji | Regenerate the emoji data from emojibase |
Project structure
Each picker has its own folder under src/: wheel, time, calendar, date, datetime, select and emoji. They build on sheet (bottom sheets) and core (theme, shared hooks and utils). Imports only flow one way: feature → sheet → core.
Every folder has the same layout:
src/select/
├─ SelectPicker.tsx components render only
├─ components/ smaller render-only parts
├─ hooks/ state and event handling
├─ select.utils.ts pure logic
├─ select.utils.test.ts unit tests for every util
├─ select.types.ts
├─ select.constants.ts
├─ select.styles.ts
├─ SelectPicker.test.tsx interaction tests
└─ index.ts the folder's public API
Where new code goes:
- Pure logic (calculations, filtering, date math) goes in
<feature>.utils.ts, with a unit test next to it. - State and event handling goes in a hook under
hooks/. - Components only render: they call hooks and utils, with no inline logic.
- Types, constants and styles go in their own
<feature>.*.tsfiles, never inside components.
Conventions
- Tests sit next to the file they test.
- Use arrow functions:
export const fn = () =>, notexport function. - Returned objects with two or more properties go one property per line. A Biome plugin enforces it, and
pnpm formatfixes it. - Imports come in three blocks separated by a blank line: React and React Native, other packages, then our own code.
- Commit messages follow Conventional Commits:
feat:,fix:,refactor(select):,docs:and so on.
Checks
The pre-commit hook formats and lints the staged files and runs typecheck, and the commit-msg hook checks the message. On every pull request, CI runs lint, typecheck, tests and the build. Pull requests are squash-merged, so CI also checks that the PR title is a Conventional Commit: it becomes the commit message on master.
Releasing
Releases are automated with release-please and driven by the commit messages on master:
fix:makes a patch release,feat:a minor one, and a!or aBREAKING CHANGE:footer a major one (a minor one before 1.0).- Commits that only touch
website/,example/,.github/orlint/don't trigger a release. - After every merge, release-please updates a
chore(release): vX.Y.Zpull request with the version bump and the changelog entry. Nothing ships while it stays open. - Merging that PR tags the version, creates the GitHub release, publishes to npm and redeploys these docs.
- To pick the version yourself, add a
Release-As: 1.0.0footer to a commit.
The docs are versioned from the git tags: /docs always matches the latest release on npm, master is published as Next at /docs/next, and every minor line keeps its own version in the dropdown.