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

@mertgulcann/rn-ui-kit

v0.1.1

Published

Cross-platform React Native UI kit with iOS-like components

Readme

@mertgulcann/rn-ui-kit

Cross-platform React Native / Expo UI kit. Components target an iOS-like look on Android; on iOS they prefer system APIs when that is the better UX.

This package is an Expo module (native stubs ready for future native APIs) plus a TypeScript UI layer.


Table of contents


Install

npm install @mertgulcann/rn-ui-kit

Because this is an Expo module, rebuild the native app after installing (dev client / EAS / npx expo prebuild + run). Expo Go may not include your linked module until you use a custom build.


Peer dependencies

| Package | Required | Notes | |---------|----------|--------| | expo | Yes | Expo Modules / autolinking | | react | Yes | | | react-native | Yes | | | react-native-reanimated | Yes | Android custom alert animations | | react-native-worklets | Yes | Used with Reanimated |

These are not bundled with the kit; your app must already provide them (typical Expo apps do).


Project structure

android/                 Expo native module (Kotlin stub)
ios/                     Expo native module (Swift stub)
expo-module.config.json
src/
  ui-components/
    alert/
      android.tsx        Custom iOS-like alert UI (Android)
      ios.tsx            Stub (iOS uses system Alert via hook)
      use-alert.tsx      Imperative API
      types.ts
  theme/                 Theme tokens + RnUiProvider
  backdrop/              Default solid backdrop

Edit Android alert visuals in src/ui-components/alert/android.tsx.
Edit iOS native alert wiring in src/ui-components/alert/use-alert.tsx.


Alert (useAlert)

Imperative confirmation / dialog API inspired by React Native’s Alert.alert, with a custom iOS-style card on Android.

Platform behavior

| Platform | What happens | |----------|----------------| | iOS | Calls native Alert.alert. No custom modal is rendered (modal is null). | | Android | Renders a centered iOS-like card (blur/dim backdrop, spring animation, hairline separators, role-colored actions). You must mount {alert.modal} in the tree. |

Quick start

import { useAlert } from "@mertgulcann/rn-ui-kit";
import { Button, View } from "react-native";

export function LogoutScreen() {
  const alert = useAlert();

  return (
    <View>
      <Button
        title="Log out"
        onPress={() =>
          alert.show({
            title: "Log out",
            message: "Are you sure you want to log out?",
            buttons: [
              { label: "Cancel", role: "cancel", onPress: () => {} },
              {
                label: "Log out",
                role: "destructive",
                onPress: () => {
                  // perform logout
                },
              },
            ],
          })
        }
      />
      {alert.modal}
    </View>
  );
}

API reference

useAlert(options?)

const alert = useAlert(options?: UseAlertOptions);

alert.show(config: AlertShowConfig): void;
alert.hide(): void;
alert.modal: React.ReactNode; // Android only; null on iOS

UseAlertOptions

| Prop | Type | Description | |------|------|-------------| | onSelectionHaptic | () => void | Called on action press / dismiss (Android custom UI). | | renderBackdrop | (opts) => ReactNode | Replaces the default solid backdrop behind the dim layer. |

Same options can also be set once on RnUiProvider.

AlertShowConfig (alert.show(...))

| Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | title | string | Yes | — | Alert title | | message | string | No | — | Supporting message | | buttons | AlertAction[] | Yes | — | Actions (can be [] for title/message-only on iOS; Android still shows the card) | | layout | "horizontal" \| "vertical" | No | auto | See Layout | | dismissOnBackdropPress | boolean | No | false | Backdrop / Android back dismiss |

AlertAction

| Field | Type | Required | Description | |-------|------|----------|-------------| | label | string | Yes | Button text (text on native iOS Alert) | | onPress | () => void | Yes | Fired when the action is chosen | | role | AlertActionRole | No | "default" | "cancel" | "destructive" | | disabled | boolean | No | Disables the action (Android custom UI) | | loading | boolean | No | Shows a spinner instead of the label (Android custom UI) |

On Android, choosing an action calls hide() first, then your onPress.

Button roles

| Role | Android look | iOS native Alert style | |------|----------------|---------------------------| | default | Accent / info color | default | | cancel | Semibold weight | cancel | | destructive | Error / red color | destructive |

Layout

Applies to the Android custom alert (iOS uses the system alert layout).

  • "vertical" — always stack actions.
  • "horizontal" or omitted — with exactly two buttons, places them side by side only if both labels fit in half-width slots (labels are measured; never truncated). Otherwise falls back to vertical.

Dismiss / backdrop

  • Default: user must tap an action (dismissOnBackdropPress: false).
  • If dismissOnBackdropPress: true:
    • Tap outside / Android hardware back dismisses.
    • Runs cancel action if one exists (role: "cancel"), otherwise just closes.
    • On iOS, maps to Alert.alert(..., { cancelable: true }).

Loading & disabled

Android only (native iOS alert has no loading spinner API):

alert.show({
  title: "Delete account",
  buttons: [
    { label: "Cancel", role: "cancel", onPress: () => {} },
    {
      label: "Delete",
      role: "destructive",
      loading: isDeleting,
      disabled: isDeleting,
      onPress: () => deleteAccount(),
    },
  ],
});

Haptics

Not bundled. Wire your own:

import * as Haptics from "expo-haptics";
import { useAlert } from "@mertgulcann/rn-ui-kit";

const alert = useAlert({
  onSelectionHaptic: () => {
    void Haptics.selectionAsync();
  },
});

Or once at the app root via RnUiProvider.

Custom backdrop (e.g. blur)

Default backdrop is a solid dim. For blur, pass renderBackdrop:

import { BlurView } from "expo-blur";
import { useAlert } from "@mertgulcann/rn-ui-kit";
import { StyleSheet } from "react-native";

const alert = useAlert({
  renderBackdrop: () => (
    <BlurView intensity={25} style={StyleSheet.absoluteFill} />
  ),
});

Theming (RnUiProvider)

useAlert works without a provider (built-in light/dark defaults follow the system color scheme).

Use RnUiProvider to align colors with your app:

import { RnUiProvider, useAlert } from "@mertgulcann/rn-ui-kit";

export function App() {
  return (
    <RnUiProvider
      themeOverride={{
        colors: {
          info: "#007AFF",
          error: "#FF3B30",
          backgroundEmphasis: "rgba(242, 242, 247, 0.92)",
        },
      }}
      onSelectionHaptic={() => {
        /* optional */
      }}
    >
      <RootNavigator />
    </RnUiProvider>
  );
}

You can also pass a full theme object (RnUiTheme: colors, spacing, radius, typography).

Full examples

Destructive confirm (logout / delete)

alert.show({
  title: "Delete note",
  message: "This cannot be undone.",
  buttons: [
    { label: "Cancel", role: "cancel", onPress: () => {} },
    { label: "Delete", role: "destructive", onPress: onDelete },
  ],
});

Single OK button

alert.show({
  title: "Saved",
  message: "Your changes were saved.",
  buttons: [{ label: "OK", role: "default", onPress: () => {} }],
});

Force vertical actions

alert.show({
  title: "Choose an option",
  layout: "vertical",
  buttons: [
    { label: "Option A", onPress: () => {} },
    { label: "Option B", onPress: () => {} },
    { label: "Cancel", role: "cancel", onPress: () => {} },
  ],
});

Dismissible info (tap outside)

alert.show({
  title: "Tip",
  message: "You can dismiss this by tapping outside.",
  dismissOnBackdropPress: true,
  buttons: [{ label: "Got it", role: "cancel", onPress: () => {} }],
});

Migration alias

import { useConfirmationModal } from "@mertgulcann/rn-ui-kit";
// identical to useAlert — deprecated alias

Related deprecated type aliases: ConfirmationModalButton, ConfirmationModalShowConfig, etc. Prefer AlertAction, AlertShowConfig, …


Native module notes

  • android/ and ios/ currently expose a minimal Expo module named RnUiKit (no native methods yet).
  • They exist so future platform APIs can be added without changing package shape.
  • UI for Alert lives in TypeScript under src/ui-components/alert/, not in the native folders.

License

MIT © M-rt4