Skip to content
Native Animation
Dialog Input Confirm animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add dialog-input-confirm

Dialog Input Confirm

A text-entry dialog with inline validation and a success check, with configurable labels, validation rules, palette, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A text-entry dialog with inline validation and a success check, with configurable labels, validation rules, palette, and fonts.

How it works

  1. The host is measured with onLayout. A dialog, capped at 420 pt, sits above its content and scrim. Keyboard frame events measure the remaining host area; short layouts scroll instead of scaling the controls.
  2. One presentationProgress controls card scale, opacity, and blur intensity. Press feedback has its own spring, so releasing a button cannot interrupt presentation.
  3. The same TextInput stays mounted through editing, invalid, submitting, error, and success. It becomes read-only during submission and success. Responses never overwrite its text or selection.
  4. A synchronous submission guard prevents duplicate saves. Each save gets an AbortSignal and a version; close, reset, background, and unmount invalidate late results. Retry reads the current field value.
  5. Status copy fades out before replacement and back in. Once an error needs helper space, that space stays reserved until the next opening, keeping the retry-to-success action row still. On success, the Lucide check draws within the existing button in 280 ms. The consumer chooses when to close.

Install

Copy this folder without demo/ into your project, preserving the platform-specific focus helper.

npx expo install @shopify/react-native-skia expo-blur react-native-reanimated react-native-worklets

Use an Expo SDK 58 project with Reanimated/Worklets configured. For web, initialize CanvasKit before importing this component, following the Skia web setup. The included paths need no icon font or remote assets. The host owns safe areas exactly once; this component adds none. The catalog supplies the demo’s top inset, and its bottom-only SafeAreaView supplies the bottom inset.

Usage

import { useRef } from 'react';
import { Button, View } from 'react-native';
import { DialogInputConfirm, type DialogInputConfirmHandle } from './dialog-input-confirm';

export function RenameExample() {
  const dialog = useRef<DialogInputConfirmHandle>(null);
  return (
    <DialogInputConfirm
      ref={dialog}
      defaultValue="Untitled"
      onSubmit={async (name, { signal }) => {
        const response = await fetch('/notes/current', {
          method: 'PATCH',
          signal,
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ name }),
        });
        return response.ok
          ? { ok: true }
          : { ok: false, message: 'Please try again.' };
      }}
    >
      <View><Button title="Rename" onPress={() => dialog.current?.open()} /></View>
    </DialogInputConfirm>
  );
}

Controlled text, custom copy, and motion knobs:

<DialogInputConfirm
  open={open}
  onOpenChange={setOpen}
  value={name}
  onValueChange={setName}
  onSubmit={saveName}
  colorScheme="dark"
  palette={{ focus: '#E3BA62', accent: '#E3BA62' }}
  labels={{ title: 'Rename note', field: 'Name', retry: 'Try again' }}
  trim
  maxLength={80}
  motion={{ durationScale: 0.85, glowOpacity: 0.12 }}
  onSuccess={updateHost}
/>

saveName returns Promise<{ ok: true } | { ok: false; message: string }> and receives { signal: AbortSignal }. Connect the signal to your transport when possible. Even if a transport ignores cancellation, stale results cannot change this dialog or fire onSuccess.

Props

PropTypeDefaultPurpose
childrenReactNode—Host content behind the dialog and Android blur target.
open, defaultOpenbooleanuncontrolled, falseControlled visibility takes precedence.
onOpenChange(open: boolean) => void—Requests visibility changes; controlled props remain authoritative.
value, defaultValuestringuncontrolled, ''Controlled text takes precedence. Defaults initialize internal state.
onValueChange(value: string) => void—Edits and reset requests.
onSubmit(value, { signal }) => Promise<SubmitResult>requiredReal async save, with immediate pending feedback.
validate(value: string) => string | null—Inline validation after the required check. A thrown validator uses labels.failure.
requiredbooleantrueBlocks empty and whitespace-only names.
trimbooleanfalseTrims validation and submission payload; never changes visible text.
maxLengthnumberunlimitedLimits user edits by Unicode code points, without splitting surrogate pairs. Does not truncate controlled/default values or split text during render.
autoFocusbooleantrueFocus field on open, respecting another active web dialog.
labelsPartial<DialogInputConfirmLabels>neutral Englishtitle, field, placeholder, cancel, submit, retry, pending, success, clear, required, failure.
onSuccess(submittedValue: string) => void—Once per accepted successful save.
onCancel() => void—Explicit close, Cancel, Back, or Escape; not reset or externally controlled closure.
closeOnSuccessbooleanfalseRequests close after success; no internal dwell timer.
keyboardType, returnKeyTypeTextInputProps types'default', 'done'Native keyboard configuration. Return submits without dismissing the keyboard.
disabledbooleanfalseBlocks all controls and handle methods except reset.
returnFocusRefRefObject<View | null>—Native trigger accessibility focus restoration. Web remembers its previous focused element.
colorScheme'light' | 'dark' | 'auto''auto'Complete Paper/Night palettes.
palettePartial<Palette>Paper/NightOverride any semantic color.
fontFamily, titleFontFamily, monoFontFamilystringsystemBody, title/buttons, and success text. No font assets required.
motion{ durationScale?, glowOpacity? }{ 1, 0.12 }Duration scale 0.5–2 and subtle success glow 0–0.3.
styleStyleProp<ViewStyle>—Root override applied last; root defaults to flex: 1.
testIDstring'dialog-input-confirm'Root plus -dialog, -field, -clear, -cancel, -submit.
accessibilityLabelstringlabels.titleDialog accessible name.

Palette tokens: background, surface, elevated, text, muted, border, accent, onAccent, accentSoft, glow, success, error, scrim, track, focus. Custom colors should preserve text/focus contrast.

Handle

open(), close(), focus(), submit(), retry(), reset(). Retry works only from error. Reset aborts the current request, requests close, restores defaultValue, and clears feedback. Controlled props only change through their callbacks. The scrim consumes touches without dismissing or canceling. A successful dialog remains open by default; call close() when the host is ready.

Non-finite motion values fall back to defaults; finite ones clamp to their documented ranges. Negative/non-finite maxLength means unlimited; fractional values round down. Unicode limits count code points, not grapheme clusters. Empty server messages use labels.failure. Existing controlled/default text is preserved even when longer than the editing limit. No lists or data IDs are involved.

Compatibility

Expo SDK 58, React 19, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, Skia 2.14.0, and expo-blur ~58.0.5. Supports iOS, Android, and web; these native packages are included in Expo Go. Reanimated 4 requires the New Architecture.

Android uses a single BlurTargetView and dimezisBlurViewSdk31Plus. Older Android versions retain the opaque surface fallback. Each material wrapper has its own clipped corner radius. Build checks establish module compatibility; they do not measure native frame rate, physical touch responsiveness, or system keyboard behavior. The component does not use a native full-screen modal: its overlay and scroll belong to its measured host. Place the host above any surrounding app content that must be occluded.

Opening, saving, and returning to the host follow the static reference pattern. Error/retry, timing, and the Night palette are original extensions; the source supplies no dark-state or motion measurements. The demo simulates a first failure and a successful retry with fixed latency. Raster assets: N/A. Icons: Lucide (ISC), copied from the local licensed kit. Demo fonts: Geist (OFL), supplied by the catalog host.

Accessibility

The native field keeps system selection, caret, keyboard, and text scaling. The dialog has modal semantics, named controls, keyboard Return submission, web Tab containment, and Escape/system Back cancellation. Native errors and success announce once; web uses live status text. Focus stays within this field through validation and save feedback. Browser focus restoration only runs while this dialog still owns focus, protecting subsequently opened dialogs.

All actions are at least 48 pt/dp. Buttons stack at large system font sizes or narrow widths, and the dialog scrolls within the measured keyboard-free region. Start/end padding follows RTL. Supply translated labels and native keyboard settings as needed. Reduced motion replaces presentation springs with a 200 ms fade and removes press scaling. There are no decorative loops.

Why this one

Use this when editing and keeping the user's text is the confirmation contract. The field and action row remain stable while validation and network feedback change around them. No success object flies back into the host, and Cancel can invalidate an in-flight save without losing the current name.