Getting started

Install Native-UI in under a minute

A single provider, one import, and your app inherits the POLPROG polish.

  1. 1

    Install the package and its peers

    npm install @polprog/native-ui react-native-reanimated react-native-safe-area-context
    
    # Optional, for DatePickerInput and DateRangePickerInput
    npm install @react-native-community/datetimepicker
  2. 2

    Load Space Grotesk (used by the default Aurora theme)

    npx expo install @expo-google-fonts/space-grotesk expo-font
  3. 3

    Wrap your app

    import { NativeUIProvider } from '@polprog/native-ui';
    
    export default function App() {
      return (
        <NativeUIProvider>
          <Root />
        </NativeUIProvider>
      );
    }
  4. 4

    Compose screens

    import { Button, Card, Heading, VStack } from '@polprog/native-ui';
    
    export function Welcome({ onStart }: { onStart: () => void }) {
      return (
        <Card>
          <VStack gap="lg">
            <Heading level={1}>Welcome</Heading>
            <Button title="Get started" onPress={onStart} />
          </VStack>
        </Card>
      );
    }

Upgrading from 1.x 2.0

Version 2.0 needs React 19, React Native 0.84+ and Reanimated 4, and removes the APIs deprecated in 1.x. The renames below cover most apps. The guide lists every change with before and after code.

// 1.x                                  // 2.0
useThemeColors()                      →  useColors()
<Button variant="destructive" />      →  <Button variant="danger" />
<Card longPressHint="…" />            →  <Card accessibilityHint="…" />
<Select selectedValue onValueChange>  →  <Select value onChange>
typography.amount / amountLarge       →  typography.mono / monoLarge

Read the full upgrade guide →

Theming

Switch light, dark or system instantly, or swap the whole look with a theme variant. Native-UI ships two: Aurora (the original) and Bloom (soft, rounded, violet). Each variant carries its own accent presets, customAccent overrides a single colour, and theme.components overrides the tokens of any component. You only pass what changes.

import { NativeUIProvider, bloomFontFamilies } from '@polprog/native-ui';

export default function App() {
  return (
    <NativeUIProvider
      config={{
        theme: 'bloom',        // 'default' (Aurora) | 'bloom'
        colorMode: 'system',   // 'light' | 'dark' | 'system'
        preset: 'violet',      // accent preset for the active variant
        fontFamilies: bloomFontFamilies,
      }}
    >
      <Root />
    </NativeUIProvider>
  );
}

Accent presets

Aurora

theme: 'default'
  • default Default #00E67A
  • midnight #818CF8
  • ocean #38BDF8
  • forest #34D399
  • sunset #FB923C
  • rose #EC4899
  • amoled #94A3B8
Preview Active

Bloom

theme: 'bloom'
  • violet Default #7C5CFF
  • grape #8B54E8
  • coral #FF5D7D
  • ocean #2F9BFF
Preview Active

Design tokens

Every surface (colour, spacing, radius, typography, shadow) is a named token. Components only read tokens, so changing a token propagates system-wide.

import { View } from 'react-native';
import { useTheme } from '@polprog/native-ui';

function Banner() {
  const theme = useTheme();

  return (
    <View
      style={{
        backgroundColor: theme.colors.primaryLight,
        padding: theme.spacing.lg,
        borderRadius: theme.borderRadius.lg,
      }}
    />
  );
}

Accessibility

Every interactive component ships with accessibility defaults: roles, labels, hit slop, screen reader announcements and reduced-motion support. Text colours are checked against WCAG contrast on every surface (minTextContrast, 4.5 by default, 7 for AAA). You can override any default per instance.

<NativeUIProvider config={{ minTextContrast: 7 }}>
  <Button
    title="Save"
    accessibilityHint="Saves your changes and moves to the next step"
    onPress={save}
  />
</NativeUIProvider>

Platform behaviour

Components adapt to the host platform: haptics, safe areas, keyboard handling and dialogs follow iOS and Android conventions, InputPrompt uses the system prompt on iOS, and screen readers can close sheets with their escape gesture. You get platform correctness without writing platform code.

// The same sheet on iOS and Android: drag it down to dismiss it.
<BottomSheet visible={open} onClose={() => setOpen(false)}>
  <BottomSheet.Header>
    <Heading level={3}>Choose a plan</Heading>
  </BottomSheet.Header>
  <BottomSheet.Body>
    <PlanList />
  </BottomSheet.Body>
</BottomSheet>

Localization New

Every string the library renders, from button hints to countdown units, comes from labels. Pass a locale and only the labels you translate. The rest stay in English. Icons and the back chevron mirror in right-to-left layouts.

<NativeUIProvider
  config={{
    locale: 'pl-PL',
    labels: { close: 'Zamknij', clear: 'Wyczyść', goBack: 'Wróć' },
  }}
>
  <Root />
</NativeUIProvider>

Changelog

  1. v2.0.0

    Stable

    Accessibility, configuration and robustness release

    • Added

      Accessibility, configuration and robustness release. Breaking: see the "Upgrading to 2.0" guide in Storybook for every change that needs code edits.

      • Removed (deprecated since 1.x): useThemeColors (use useColors), Button variant="destructive" (use danger), typography amount / amountLarge / amountSmall (use mono*), Card longPressHint (use accessibilityHint), Select selectedValue / onValueChange (use value / onChange), unused Select tokens (titlePadding*, cancelPaddingVertical, cancelMarginTop, minBottomPadding).
      • Peer ranges now start at verified versions: React 19, React Native 0.84, Reanimated 4 (required), Safe Area Context 5, DateTimePicker 9.
      • usePressableScale always uses Reanimated; its result no longer has isReanimated and animatedStyle is a Reanimated animated style.
      • Color: new onPrimary, onSuccess, onWarning, onError, onInfo tokens computed with WCAG contrast (minTextContrast, default 4.5). Light-mode primary buttons now use dark text where white was unreadable (1.7:1 on the default green). textInverse is an alias of onPrimary. The default light error is now #DC2626 (white on the old #EF4444 was 3.8:1). getContrastText uses WCAG luminance. New helpers: contrastRatio, resolveOnColor, withAlpha.
      • +18 more…
  2. v1.7.2

    Stable
    • Fixed

      Fix type checking against React Native 0.87 and newer. Box and Stack no longer assign to readonly style properties, and the ref of Input, TextArea and SearchBar is typed as the TextInput instance via ComponentRef<typeof TextInput>, which matches older React Native versions as well. Input, TextArea and PressableScale now declare their exported types explicitly, so the published declarations no longer depend on React Native internal type names.

See full changelog →

Expo React Native 0.84+ iOS Android TypeScript Accessible