logoChisa UI

Command Palette

Search for a command to run...

Componentschevron_rightAnimatedInput

AnimatedInput

Input component with 4 variants (outlined, filled, underlined, rounded), floating label animation, and error shake effect.

9:41
signal_cellular_altwifibattery_full
outlined
Email
filled
Username
underlined
Phone
rounded
Password

Installation

$

Usage

Example.tsx
import { View } from 'react-native';
import { AnimatedInput } from './components/AnimatedInput';
export default function App() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
return (
<View style={{ padding: 16 }}>
{/* Outlined (default) */}
<AnimatedInput
label="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
/>
{/* Filled variant */}
<AnimatedInput
label="Password"
variant="filled"
value={password}
onChangeText={setPassword}
secureTextEntry
showPasswordToggle
/>
{/* With error */}
<AnimatedInput
label="Username"
variant="underlined"
error={error}
/>
{/* Rounded variant */}
<AnimatedInput
label="Search"
variant="rounded"
placeholder="Search..."
/>
</View>
);
}

Full Component Code

AnimatedInput.tsx
1import React, { useState, useRef } from 'react';
2import { View, Text, TextInput, StyleSheet, Pressable, TextInputProps } from 'react-native';
3import Animated, { useSharedValue, useAnimatedStyle, withTiming, withSpring, interpolate, interpolateColor, Easing } from 'react-native-reanimated';
4
5type InputVariant = 'outlined' | 'filled' | 'underlined' | 'rounded';
6
7interface AnimatedInputProps extends Omit<TextInputProps, 'style'> {
8 label?: string;
9 variant?: InputVariant;
10 error?: string;
11 helperText?: string;
12 primaryColor?: string;
13 disabled?: boolean;
14}
15
16export const AnimatedInput: React.FC<AnimatedInputProps> = ({
17 label,
18 variant = 'outlined',
19 error,
20 helperText,
21 primaryColor = '#cf350b',
22 disabled = false,
23 value,
24 onChangeText,
25 ...props
26}) => {
27 const inputRef = useRef<TextInput>(null);
28 const [isFocused, setIsFocused] = useState(false);
29 const focusProgress = useSharedValue(0);
30 const labelPosition = useSharedValue(value ? 1 : 0);
31 const shakeAnimation = useSharedValue(0);
32
33 const handleFocus = () => {
34 setIsFocused(true);
35 focusProgress.value = withTiming(1, { duration: 200 });
36 labelPosition.value = withTiming(1, { duration: 200 });
37 };
38
39 const handleBlur = () => {
40 setIsFocused(false);
41 focusProgress.value = withTiming(0, { duration: 200 });
42 if (!value) labelPosition.value = withTiming(0, { duration: 200 });
43 };
44
45 React.useEffect(() => {
46 if (error) {
47 shakeAnimation.value = withSpring(1, { damping: 2, stiffness: 400 }, () => {
48 shakeAnimation.value = withTiming(0, { duration: 100 });
49 });
50 }
51 }, [error]);
52
53 const animatedLabelStyle = useAnimatedStyle(() => ({
54 transform: [
55 { translateY: interpolate(labelPosition.value, [0, 1], [0, -24]) },
56 { scale: interpolate(labelPosition.value, [0, 1], [1, 0.85]) },
57 ],
58 color: isFocused ? primaryColor : '#6B7280',
59 }));
60
61 const animatedBorderStyle = useAnimatedStyle(() => ({
62 borderColor: interpolateColor(focusProgress.value, [0, 1], ['#D1D5DB', error ? '#EF4444' : primaryColor]),
63 borderWidth: interpolate(focusProgress.value, [0, 1], [1, 2]),
64 }));
65
66 return (
67 <View style={styles.wrapper}>
68 <Pressable onPress={() => inputRef.current?.focus()}>
69 <Animated.View style={[styles.container, animatedBorderStyle]}>
70 {label && <Animated.Text style={[styles.label, animatedLabelStyle]}>{label}</Animated.Text>}
71 <TextInput
72 ref={inputRef}
73 style={styles.input}
74 value={value}
75 onChangeText={onChangeText}
76 onFocus={handleFocus}
77 onBlur={handleBlur}
78 editable={!disabled}
79 {...props}
80 />
81 </Animated.View>
82 </Pressable>
83 {(error || helperText) && <Text style={[styles.helper, error && { color: '#EF4444' }]}>{error || helperText}</Text>}
84 </View>
85 );
86};
87
88const styles = StyleSheet.create({
89 wrapper: { marginBottom: 16 },
90 container: { minHeight: 56, paddingHorizontal: 16, justifyContent: 'center', borderRadius: 12, backgroundColor: '#FFF' },
91 label: { position: 'absolute', left: 16, fontSize: 16, color: '#6B7280' },
92 input: { fontSize: 16, color: '#1F2937', paddingTop: 12 },
93 helper: { marginTop: 4, marginLeft: 16, fontSize: 12, color: '#6B7280' },
94});

Props

NameTypeDefaultDescription
labelstring-Floating label text
variant'outlined' | 'filled' | 'underlined' | 'rounded''outlined'Visual variant
errorstring-Error message (triggers shake)
helperTextstring-Helper text below input
leftIconReact.ReactNode-Left icon component
rightIconReact.ReactNode-Right icon component
showPasswordTogglebooleanfalseShow password visibility toggle
primaryColorstring'#E16C2D'Focus/active color
disabledbooleanfalseDisabled state