Skip to content
Native Animation
Tag Token Input animation preview

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

npx nativeanimation add tag-token-input

Tag Token Input

A tag input with suggestion flights and animated token creation, with configurable suggestions, placeholder, token colors, 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 tag input with suggestion flights and animated token creation, with configurable suggestions, placeholder, token colors, and fonts.

Why this one

Ordinary tag fields insert pills abruptly. Here, the typed label shrinks from 17 to 14 pt while its pill material grows around it. Suggestions travel from their measured screen positions. Empty-field Backspace selects the last token first; a second Backspace dissolves it into six particles. Duplicate tags highlight the existing token instead of creating clutter.

How it works

  1. Normalize labels and update logical data only for input events. React never updates for animation frames.
  2. Measure the draft or suggestion with measureInWindow, then measure the destination token on layout. Their difference supplies the actual flight origin, including horizontal suggestion scrolling.
  3. Derive flight, text size, and material expansion from one shared entrance progress. Entrance springs use damping 26, stiffness 240, and mass 1. Layout springs use damping 50, stiffness 240, and effective mass 4.
  4. Animate wrapping layout when tokens change lines. Each token has an independent press scale so release cannot cancel a flight.
  5. Keep a deleted token in its original slot until its shared burst progress finishes. Notify JS through scheduleOnRN, then let the remaining layout settle.
  6. Replace flights and particles with 200 ms fades for reduced motion. Announce additions, selection, duplicates, limits, and removals.

Install

npx expo install expo-image react-native-reanimated react-native-worklets

Copy the four files listed in piece.json. No demo, theme import, assets, Skia initialization, or additional native configuration is needed. Icons: Lucide (ISC), copied from the project kit and rendered as SVG image sources.

Usage

import { TagTokenInput } from './tag-token-input';

<TagTokenInput
  initialTags={['research']}
  suggestions={['ideas', 'reading-list']}
  onChange={tags => storeTags(tags)}
/>

With appearance and external controls:

const tagsRef = useRef<TagTokenInputRef>(null);

<TagTokenInput
  ref={tagsRef}
  maxTags={8}
  accentColor="#8B5CF6"
  tagColors={['#7C3AED', '#0F766E', '#B45309']}
  colorScheme="light"
  fontFamily="YourInterfaceFont"
  monoFontFamily="YourMonoFont"
  style={{ flex: 0 }}
/>
// In a button handler:
tagsRef.current?.choose('ideas');

The host loads any custom fonts. The component defaults to system fonts. Give the container available width; it uses onLayout, not window dimensions. The demo uses the shared Geist, Instrument Serif, and Geist Mono fonts.

Props

PropTypeDefaultDescription
initialTagsstring[][]Initial normalized, unique labels; reset restores these.
suggestionsstring[][]Horizontally scrollable choices.
maxTagsnumber8Maximum committed labels.
accentColorstring#8B5CF6Selection and focus color.
tagColorsstring[]Violet, teal, amber, blueDeterministic token colors; use six-digit hex colors.
fontFamilystringSystemLabel and input font.
monoFontFamilystringSystemCount font.
placeholderstringAdd a tag…Empty draft hint.
colorSchemelight / dark / autoautoSurface and text appearance.
onChange(tags: string[]) => void—Logical additions and removals.
onInteraction() => void—Touch, editing, focus, or suggestion selection.
accessibilityLabelstringTagsText field label.
styleStyleProp<ViewStyle>—Root overrides; root defaults to flex: 1.
testIDstring—Root test identifier.
durationnumber350Tag-limit feedback fade duration in milliseconds. Spring physics and reduced-motion timing are unchanged.
initialTagsstring[][]Starting labels.
suggestionsstring[][]Suggested labels below the field.
maxTagsnumber8Maximum number of tags.
accentColorstring'#8B5CF6'Accent for selection and focus.
tagColorsstring[]COLORSColors assigned deterministically to labels.
fontFamilystringundefinedInterface typeface.
monoFontFamilystringundefinedTypeface for the counter.
placeholderstring'Add a tag…'Empty draft hint.
colorScheme'light' | 'dark' | 'auto''auto'Light, dark, or device appearance.
onChange(tags: string[]) => voidundefinedReports committed labels.
onInteraction() => voidundefinedCalled when the user interacts, allowing autoplay to stop.
accessibilityLabelstring'Tags'Screen-reader field label.
styleStyleProp<ViewStyle>undefinedOuter container overrides.
testIDstringundefinedTest identifier.

Ref methods: add(label), choose(label), setDraft(text), commit(), backspace(), reset(), and focus(). Labels are case-insensitive, have their initial hash removed, and are limited to 24 characters. Enter, comma, and space commit input. The field is uncontrolled after initialization; onChange provides the current logical labels.

Compatibility

Expo SDK 58; React Native 0.88.0; Reanimated 4.7.1; worklets 0.13.0. Compatible with the installed Expo Go modules. iOS, Android, and web are compilation targets. No Skia dependency.

Accessibility

All buttons have roles and labels. Removal targets and suggestions are at least 44 pt. Backspace selection is announced, and every token also has a direct remove button. Important changes use polite live regions and screen-reader announcements. Reduced motion eliminates flights, shaking, particles, and layout travel; additions and deletions fade over 200 ms. The counter fades without rolling digits. There are no blurs or trails.

Demo timeline and review notes

Autoplay types design at 420–845 ms, commits around 1.05 s, and flies ideas around 1.8 s. It highlights a duplicate, selects the last tag, and deletes it before repeating every 4.9 s. Any user touch or editing stops the loop; timers are cleared on unmount.

The Lumen tile uses the kit's lamp glyph and amber-to-orange gradient. The note and editor use white surfaces, double shadows, and violet accents. The top right is reserved for the viewer close button. At 390×844 and 320×694, content remains in a wrapping column with scroll fallback rather than fixed vertical coordinates. The tag field stays full width; important labels are not clipped. All effects belong to pill material or individual particles; no opaque effect block crosses text.