StatsHub Docs

ui-native

The React Native component kit — deep imports, no barrel, and the three seams it deliberately leaves to the app.

@statshub/ui-native is the React Native component kit that expo renders with. It was lifted out of the app, where it had lived as an ordinary directory of components, and the app now reaches it by name across roughly 550 call sites.

Importing

Deep paths, always.

import { Button } from "@statshub/ui-native/button/button";
import { useSurfaceStyle } from "@statshub/ui-native/sheet/surface-style";

There is no barrel and no main, deliberately. A root index.ts re-exporting 140 modules is one import away from pulling the whole kit into a bundle that wanted one button, and Metro does not tree-shake its way back out.

Why the files sit at the package root

Metro's package-exports wildcards must name an exact extension. A "./*": "./src/*" map does not resolve, and an array fallback resolves for tsc but not for Metro. This kit mixes .ts and .tsx, so it ships no exports field at all and lets Metro's ordinary file lookup work — which is also why the component directories are at the package root rather than under src/.

button.tsx
package.json — no main, no exports

Note the absence of src/ and of an index.ts. Both are consequences of the box above, not oversights.

What it must not know

The kit renders. It does not decide where a failure is reported or how the app talks to disk. Three seams exist because of that, and each has an app-side half:

SeamThe kit ownsThe app owns
ErrorsNothingRootErrorBoundary around UIProvider, AppErrorBoundary as its children
Surface styleThe store (sheet/surface-style)Reading it at launch, writing each change
Route presentationThe RoutePresentation vocabularyWhich route gets which presentation

A kit file that needs @/lib/... is the smell: either the thing it needs belongs in the kit too, or the component belongs in the app. Both answers are fine; a package importing from an app is not.

Theme

_core/_theme holds the uniwind CSS entry and the generated .d.ts. apps/statshub-expo/metro.config.js addresses both by path from the workspace root, and the app's tsconfig.json names the .d.ts explicitly — without that, every className in the app stops typechecking.

See also platform differences and add a UI component.

On this page