Skip to content
React NativeBeta

Spinner

Loading indicators with a delay so fast loads never flash.

Beta. React Native support is in beta. APIs may change or break between prereleases (npm tag native).

On this page
Web preview — real device may differ
tsx
import { useState } from "react";
import { Text, View } from "react-native";
import { Button } from "@/component-lib/button";
import { useKitTheme } from "@/component-lib/shared/native";
import { Spinner } from "@/component-lib/spinner";
import { Row, Section } from "./layout"; // Section and Row: page layout for the demo
 
export function SpinnerDemo() {
  const { tokens } = useKitTheme();
  const [busy, setBusy] = useState(false);
  return (
    <>
      <Section title="Spinner · sizes">
        <Row>
          <Spinner size="xs" />
          <Spinner size="sm" />
          <Spinner size="md" />
          <Spinner size="lg" />
          <Spinner size="xl" />
          <Spinner size={56} color={tokens.success} />
        </Row>
      </Section>
 
      <Section title="Spinner · dots and labels">
        <Row>
          <Spinner variant="dots" size="lg" />
          <Spinner variant="dots" size="xl" color={tokens.danger} />
          <Spinner showLabel label="Fetching invoices…" />
          <Spinner variant="dots" showLabel />
        </Row>
      </Section>
 
      <Section title="Spinner · covering content (delay 150 ms, min 600 ms)">
        <Spinner loading={busy} delay={150} minDuration={600} style={{ borderRadius: tokens.radius }}>
          <View style={{ padding: 16, borderRadius: tokens.radius, borderWidth: 1, borderColor: tokens.border, gap: 10 }}>
            <Text style={{ color: tokens.fg, fontSize: tokens.fontSize, fontWeight: "600" }}>Monthly report</Text>
            <Text style={{ color: tokens.muted, fontSize: tokens.fontSize }}>
              While loading, this panel takes no touches and is hidden from screen readers.
            </Text>
            <Button variant="outline" size="sm" onPress={() => setBusy(true)}>
              Refresh
            </Button>
          </View>
        </Spinner>
        <Button variant="secondary" onPress={() => setBusy((b) => !b)}>
          {busy ? "Stop loading" : "Start loading"}
        </Button>
      </Section>
    </>
  );
}

Install

npx @grampro/kit@native add Spinner

Usage

tsx
import { Spinner } from "@/component-lib/spinner";
 
<Spinner loading={pending} delay={200} label="Loading invoices" />

Props

SpinnerProps

PropTypeDescription
loadingbooleanDefault true. With children, the spinner covers them while this is true.
sizeSpinnerSize | numberDefault md (24 dp); a number is dp.
variantSpinnerVariantDefault ring.
colorstringColour of the indicator. Default: the accent colour (the web uses currentColor).
labelstringWhat screen readers announce. Default "Loading".
showLabelbooleanShow the label next to the spinner.
delaynumberMilliseconds to wait before showing, so fast work doesn't flash. Default 0.
minDurationnumberOnce shown, stay at least this many milliseconds. Default 0.
childrenReactNodeContent to cover while loading. It stops taking touches and is hidden from screen readers.
styleStyleProp<ViewStyle>
stylesPartial<{ root: StyleProp<ViewStyle>; status: StyleProp<ViewStyle>; indicator: StylePro…
localeTextPartial<SpinnerLocaleText>

Also accepts the props of Omit<ViewProps, "children" | "style">.

Native and web

  • Takes a color (native has no currentColor); defaults to the accent.
  • Web version: Spinner on the web.