Success check

Free · MITReact Native · Expo

A success or error moment: the circle springs in, the check draws itself while a ring pulses out and the title rises in; the error variant draws a cross and shakes once. Inline or fullscreen, optionally with confetti.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/success-check.json

Then run npx expo install --fix so react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/success-check.

Usage

import { useState } from "react";
import { Button } from "@/components/goodui-native/button/button";
import { SuccessCheck } from "@/components/goodui-native/success-check/success-check";

export function SendMoney({ send }: { send: () => Promise<void> }) {
  const [done, setDone] = useState(false);
  return (
    <>
      <Button onPress={async () => { await send(); setDone(true); }}>Send $1,250.00</Button>
      <SuccessCheck
        variant="fullscreen"
        visible={done}
        onDismiss={() => setDone(false)}
        title="Money sent"
        subtitle="$1,250.00 to Maya Chen"
        confetti
      />
    </>
  );
}

When to use

Use it for
  • The end of a flow: payment sent, booking confirmed, account created, workout saved.
  • A result that replaces a form or a sheet's content once it is submitted.
  • A failed final step that needs a clear, calm error moment before offering a retry.
Reach for something else
  • A single button's result: Button and IconButton already show success and error themselves.
  • Background events: use toast.
  • A problem that leaves the screen usable: use banner.

Examples

Inline, replacing a sheet's form

{submitted ? (
  <SuccessCheck title="Booked" subtitle="Thu 14 Nov, 10:30 with Dr. Patel">
    <Button variant="secondary" onPress={close}>Done</Button>
  </SuccessCheck>
) : form}

Error, then retry

<SuccessCheck status="error" size="sm" title="Card declined" subtitle="Try another card" playKey={attempt} />

API reference

PropTypeDefault
status
`success` draws a check and pulses a ring; `error` draws a cross and shakes.
"success" | "error""success"
variant
`inline` sits in your layout; `fullscreen` covers the screen through the portal and closes itself.
"inline" | "fullscreen""inline"
visible
Plays the moment each time it turns `true`. Fullscreen closes with a fade when it turns `false`. Default `true`.
booleantrue
title
One short line: "Payment sent", "Booked". Rises in under the mark.
string—
subtitle
Detail under the title: an amount, a time, a reference.
string—
size
Circle of 48, 72 or 96 pt. Default `md` inline, `lg` fullscreen.
"sm" | "md" | "lg"—
playKey
Change it to play the moment again without hiding it.
string | number—
duration
Fullscreen: milliseconds before `onDismiss` runs on its own. `Infinity` waits for a tap. Default 2200.
number2200
onDismiss
Fullscreen: called after `duration`, on a tap, on Escape or the Android back button. Set `visible` to false here.
() => void—
confetti
Burst confetti out of the circle on success. Default `false`.
booleanfalse
haptic
Success or error haptic as the mark lands. Default `true`.
booleantrue
children
Extra content that rises in last, for example a Done button (inline) or a receipt line.
ReactNode—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<SuccessCheckLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Success: the circle scales in on the bouncy spring, the check draws itself in (260 ms, from 170 ms), a ring pulses out to 1.75× and fades over 700 ms, then title, subtitle and children rise 12 pt on the smooth spring, staggered.
  • Error: the circle scales in on the snappy spring, the two strokes of the cross draw one after another, then the mark shakes once.
  • It plays again each time visible turns true, the status changes or playKey changes.
  • Fullscreen fades its background in and out over the app.
  • Reduced motion: circle and text fade in, the check appears complete, no ring, no shake, no confetti physics (Confetti shows its static sparkle).
  • Haptic: success (celebrate with confetti) or error when the mark lands (about 400 ms in), once per play. haptic={false} turns it off.
  • A handful of Reanimated values on the UI thread. Confetti mounts only when the burst fires and unmounts when it ends.

Accessibility

  • Each time it plays it announces the title and subtitle (or 'Done' / 'Something went wrong').
  • Fullscreen is modal (accessibilityViewIsModal): a single button that closes it on tap, with the hint 'Closes this message'; Escape on the web and the Android back button close it too.
  • Colors come from success and danger with their on-colors; the mark is never the only signal when a title is set.

Notes for AI

  • Inline: render it where the result belongs; visible={false} removes it. Fullscreen: control visible and set it false in onDismiss.
  • Fullscreen closes after duration ms (default 2200); pass duration={Infinity} to wait for a tap.
  • Use playKey to replay without hiding, for example the attempt number for repeated errors.
  • children rise in last; inline that is the place for a Done button. In fullscreen the whole screen is already the close button, so pass text, not buttons.