API

nsh/device

nsh/device tells the app about the device and its environment. windowSize, colorScheme and fontScale are reactive accessors: read them in JSX, computed or effect, and what reads them updates when the platform reports a change (rotation, a window resize, switching to dark mode, changing the text size).

import { colorScheme, fontScale, I18nManager, PixelRatio, Platform, windowSize } from "nsh/device";

See Device and environment for the guide.

#windowSize

const windowSize: Accessor<{ width: number; height: number }>;

The size of the surface in points: the window, or the embedded NshAppView / NshView. Reactive.

const wide = () => windowSize().width >= 600;

<View style={{ flexDirection: wide() ? "row" : "column" }}>{/* ... */}</View>

#colorScheme

const colorScheme: Accessor<"light" | "dark">;

The system appearance. Reactive. Tailwind’s dark: variant follows it too (see Tailwind).

const dark = () => colorScheme() === "dark";

<View style={{ flex: 1, backgroundColor: dark() ? "#000" : "#fff" }} />

#fontScale

const fontScale: Accessor<number>;

The user’s preferred text size relative to the default (1 is the default size; Dynamic Type on iOS, font size on Android). Reactive. Text already scales with it unless allowFontScaling={false}; read it when other sizes should follow, such as icons next to text.

#Platform

const Platform: {
  readonly OS: "ios" | "android" | "macos" | "windows" | "linux";
  readonly isDesktop: boolean;
  readonly Version: string | number;
  readonly isPad: boolean;
  readonly isTV: boolean;
  readonly isMacCatalyst: boolean;
  readonly constants: {
    readonly osVersion: string | number;
    readonly interfaceIdiom: "phone" | "pad" | "mac" | "tv" | "desktop";
  };
  select<T>(spec: PlatformSelectSpec<T>): T | undefined;
};

The platform the app runs on, constant while it runs.

MemberDescription
OSThe operating system. iOS, the iOS simulator and Mac Catalyst report "ios".
isDesktopA desktop OS (macOS, Windows, Linux).
VersioniOS and desktops: the version string ("17.5"); Android: the API level (a number).
isPadA tablet: an iPad, or an Android tablet.
isTVA TV (Apple TV idiom, Android TV).
isMacCatalystMac Catalyst in the Mac idiom.
constants.osVersionThe same as Version.
constants.interfaceIdiom"phone", "pad", "mac", "tv" or "desktop".
select(spec)The value for this platform (see below).

#Platform.select

interface PlatformSelectSpec<T> {
  ios?: T;
  android?: T;
  macos?: T;
  windows?: T;
  linux?: T;
  mobile?: T;   // iOS and Android
  desktop?: T;  // macOS, Windows and Linux
  native?: T;   // every platform
  default?: T;
}

Returns the value for the current OS (ios, android, …), else mobile / desktop, else native, else default, else undefined.

const shadow = Platform.select({
  ios: { shadowColor: "#000", shadowOpacity: 0.15, shadowRadius: 8 },
  android: { elevation: 4 },
});

#PixelRatio

const PixelRatio: {
  get(): number;
  getFontScale(): number;
  getPixelSizeForLayoutSize(size: number): number;
  roundToNearestPixel(size: number): number;
};

Device pixels per point.

MethodDescription
get()Device pixels per point (e.g. 3 on most iPhones).
getFontScale()The same as fontScale(), without tracking.
getPixelSizeForLayoutSize(size)size points in whole device pixels.
roundToNearestPixel(size)size rounded to the nearest device pixel, in points.
const hairline = 1 / PixelRatio.get();

PixelRatio is not reactive. A window can move to another screen (on Mac Catalyst, or an external display); read it in components that also read windowSize(), so they run again when the window changes.

#I18nManager

const I18nManager: {
  isRTL: boolean;                  // reactive
  doLeftAndRightSwapInRTL: true;
  allowRTL(allow: boolean): void;
  forceRTL(force: boolean): void;
  getConstants(): { isRTL: boolean; doLeftAndRightSwapInRTL: boolean };
};

Right-to-left layout. In right-to-left layout, rows run from the right, and left / right style properties (marginLeft, paddingRight, …) mean start and end. Changes apply at once; no restart is needed.

MemberDescription
isRTLWhether the app lays out right to left. Reading it is tracked.
doLeftAndRightSwapInRTLAlways true: left and right swap in right-to-left layout.
allowRTL(allow)Follow right-to-left languages (default true).
forceRTL(force)Lay out right to left whatever the language.
getConstants(){ isRTL, doLeftAndRightSwapInRTL }.
<Text onPress={() => I18nManager.forceRTL(!I18nManager.isRTL)}>
  {I18nManager.isRTL ? "Right to left" : "Left to right"}
</Text>
  • Safe area insets and the keyboard (safeArea(), keyboard(), useSafeAreaInsets()) are part of nsh/ui/mobile.
  • Screen reader and reduced motion settings are in nsh/accessibility.