Native UI components for React Native. Each component is a genuine platform view — a real
UICollectionView, not a reimplementation of one — driven by a Radix-style compound API.
| Package | What it is |
|---|---|
@rngui/collection-view |
A real UICollectionView on iOS, a real RecyclerView + Material 3 Expressive on Android |
Install only what you use:
npm install @rngui/collection-viewThere is deliberately no umbrella package that re-exports every component. Native code doesn't tree-shake: React Native's autolinking walks an app's transitive dependencies and compiles and links every podspec and Kotlin source set it finds, whether or not you ever import the component. An umbrella would make every app pay the full build time and binary size of the whole family to use one view.
The exception, whenever there is enough shared code to justify extracting it, would be a
pure-JavaScript @rngui/core: no podspec, no android/, no react-native.config.js, so
autolinking cannot see it at all and it costs nothing to depend on. It does not exist yet —
one component is not enough consumers to design a shared layer against.
packages/* one publishable component each
apps/example/ a single kitchen-sink app that imports them all
One example app rather than one per package — prebuild + pod install + codegen is the
slowest loop in the repo, and there is no reason to pay it per component.
Five screens, each chosen because it is hard in a different way:
| Screen | What it is for |
|---|---|
| Settings | The three stock cell kinds, a hosted React child, and thirty numbered rows to scroll through pooled cells |
| Reminders | Every row kind — switches, text fields, an auto-growing text area, menus, inline date pickers, swipe actions — plus keyboard avoidance and a formSheet |
| Contacts | 2,000 rows, plain with pinned section headers, and the A–Z scrubber |
| Health | A horizontally scrolling chip strip inside a vertical list, gradient background, recycling Card rows beside a one-off hosted chart |
| Custom | Theming played with live, a self-measuring hosted row, a @gorhom/bottom-sheet integration, and 200 windowed hosted rows |
A blurred large-title header is the default on every one of them, deliberately: it is the hardest case for a custom scroll view, so every screen is a live test that UIKit still finds the collection view and collapses the title against it.
Nitro is a better authoring experience for almost everything, but it cannot host React
children inside a recycled cell. Nitro assigns its view as a Fabric component's
contentView, which means React's children arrive as siblings, and
RCTViewComponentView's default mount/unmount assert that a child is exactly where React
put it — so a hosted view can only be floated over its cell, never placed inside it.
A hand-written Fabric component view overrides those two methods and owns child placement
outright, which is how react-native-screens reparents children into view controllers and
navigation bars. That makes <CollectionView.Host> a real subview of cell.contentView —
clipped, hit-tested and scrolled by UIKit for free.
npm install # links every workspace
npm run verify # typecheck, regenerate both native models, run every unit test
npm run verify:clean # the same, against a fresh clone of HEAD in a temp directory
npm run ios # builds and runs the example on a simulator
npm run android # ditto, on an emulator
npm run lint # prettier --checkverify covers the Kotlin tests too, but they need apps/example/android — which expo prebuild
generates and git does not track — and an ANDROID_HOME. Without both it says so and skips them
rather than failing, so a machine with only Node and Xcode still verifies everything else.
verify:clean answers the question verify cannot: whether the commit stands up without this
machine. It clones HEAD to a temp directory, npm cis from the lockfile, prebuilds Android from
nothing and runs everything there — so a dependency missing from the lockfile, or generated Kotlin
that only compiles against a stale prebuild, fails here rather than on someone else's clone. Around
half a minute with a warm npm cache. Only committed work is in the clone, and it warns when the
checkout is dirty. It does not cover Linux, which is the one thing left for CI.
The same checks run in CI as the Verify workflow, which is
manual only: this repository is private, a macOS runner costs ten times a Linux one, and the
Kotlin job pays for a prebuild before it can assert anything. Start it from the Actions tab before
merging a branch or cutting a release — the Swift and Kotlin jobs each have a checkbox, so a
TypeScript-only change need not pay for either. CI passes --require, so there a skipped Kotlin
run is a failure rather than a shrug.
Packages are consumed from source: each points source/react-native at src/index,
and the example's Metro config watches the repo root. Editing TypeScript needs no rebuild;
editing Swift or Kotlin does. A change to a codegen spec needs pod install before the next
iOS build, because that is when the generated Props.h is rewritten.
packages/collection-view/src/tree.ts is the single source of truth for the descriptor tree,
and npm run gen compiles it into Swift Codable structs plus a golden fixture and the
assertions that check every field round-trips. The output is committed, so pod install
never needs Node and a schema change shows up in a diff — and npm run verify fails if it is
stale.
The tests are built by SwiftPM rather than Xcode (swift test, seconds, no simulator), which
is possible only because the generated model depends on nothing but Foundation.
AGPL-3.0-only. See LICENSE.