or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

animated-components.mdanimation-functions.mdconfiguration-utilities.mdcore-reactive-system.mdcss-integration.mdevent-handling.mdindex.mdinterpolation-easing.mdlayout-animations.mdplatform-functions.mdscreen-transitions.mdtesting-utilities.mdworklet-functions.md

event-handling.mddocs/

0

# Event Handling

1

2

Optimized handlers for user interactions, scrolling, device events, and other reactive patterns that run efficiently on the UI thread.

3

4

## Capabilities

5

6

### Scroll Event Handling

7

8

High-performance scroll event handlers that can update shared values on the UI thread.

9

10

```typescript { .api }

11

/**

12

* Creates an optimized scroll event handler that runs on the UI thread

13

* @param handler - Scroll handler function or configuration object

14

* @returns Processed scroll handler for use with ScrollView components

15

*/

16

function useAnimatedScrollHandler<T>(

17

handler: ScrollHandler<T> | ScrollHandlers<T>

18

): ScrollHandlerProcessed<T>;

19

20

type ScrollHandler<T> = (event: ScrollEvent, context?: T) => void;

21

22

interface ScrollHandlers<T> {

23

onScroll?: ScrollHandler<T>;

24

onBeginDrag?: ScrollHandler<T>;

25

onEndDrag?: ScrollHandler<T>;

26

onMomentumBegin?: ScrollHandler<T>;

27

onMomentumEnd?: ScrollHandler<T>;

28

}

29

30

interface ScrollEvent {

31

contentOffset: { x: number; y: number };

32

contentSize: { width: number; height: number };

33

layoutMeasurement: { width: number; height: number };

34

targetContentOffset?: { x: number; y: number };

35

velocity?: { x: number; y: number };

36

zoomScale?: number;

37

}

38

```

39

40

**Usage Examples:**

41

42

```typescript

43

import React from "react";

44

import Animated, {

45

useSharedValue,

46

useAnimatedScrollHandler,

47

useAnimatedStyle,

48

interpolate,

49

Extrapolation

50

} from "react-native-reanimated";

51

52

const ScrollHandlerExample = () => {

53

const scrollY = useSharedValue(0);

54

const headerHeight = 200;

55

56

// Simple scroll handler

57

const scrollHandler = useAnimatedScrollHandler({

58

onScroll: (event) => {

59

scrollY.value = event.contentOffset.y;

60

},

61

});

62

63

// Advanced scroll handler with multiple events

64

const advancedScrollHandler = useAnimatedScrollHandler({

65

onScroll: (event) => {

66

scrollY.value = event.contentOffset.y;

67

},

68

onBeginDrag: (event) => {

69

console.log("Started dragging");

70

},

71

onEndDrag: (event) => {

72

console.log("Stopped dragging");

73

},

74

onMomentumBegin: (event) => {

75

console.log("Momentum scroll started");

76

},

77

onMomentumEnd: (event) => {

78

console.log("Momentum scroll ended");

79

},

80

});

81

82

// Parallax header style

83

const headerStyle = useAnimatedStyle(() => ({

84

transform: [

85

{

86

translateY: interpolate(

87

scrollY.value,

88

[0, headerHeight],

89

[0, -headerHeight / 2],

90

Extrapolation.CLAMP

91

),

92

},

93

],

94

opacity: interpolate(

95

scrollY.value,

96

[0, headerHeight / 2, headerHeight],

97

[1, 0.5, 0],

98

Extrapolation.CLAMP

99

),

100

}));

101

102

return (

103

<>

104

{/* Parallax header */}

105

<Animated.View

106

style={[

107

{

108

position: "absolute",

109

top: 0,

110

left: 0,

111

right: 0,

112

height: headerHeight,

113

backgroundColor: "lightblue",

114

zIndex: 1,

115

},

116

headerStyle,

117

]}

118

>

119

<Animated.Text>Parallax Header</Animated.Text>

120

</Animated.View>

121

122

{/* Scrollable content */}

123

<Animated.ScrollView

124

onScroll={scrollHandler}

125

scrollEventThrottle={16}

126

style={{ paddingTop: headerHeight }}

127

>

128

{/* Content items */}

129

{Array.from({ length: 50 }).map((_, index) => (

130

<Animated.View

131

key={index}

132

style={{

133

height: 80,

134

backgroundColor: index % 2 ? "white" : "lightgray",

135

justifyContent: "center",

136

alignItems: "center",

137

}}

138

>

139

<Animated.Text>Item {index}</Animated.Text>

140

</Animated.View>

141

))}

142

</Animated.ScrollView>

143

</>

144

);

145

};

146

```

147

148

### Scroll Offset Tracking

149

150

Utilities for tracking scroll position with animated refs.

151

152

```typescript { .api }

153

/**

154

* Tracks scroll offset of a scrollable component

155

* @param scrollableRef - Animated ref to the scrollable component

156

* @returns SharedValue containing the current scroll offset

157

*/

158

function useScrollOffset(scrollableRef: AnimatedRef<any>): SharedValue<number>;

159

160

/**

161

* @deprecated Use useScrollOffset instead

162

*/

163

function useScrollViewOffset(scrollableRef: AnimatedRef<any>): SharedValue<number>;

164

```

165

166

**Usage Example:**

167

168

```typescript

169

import React, { useRef } from "react";

170

import Animated, {

171

useAnimatedRef,

172

useScrollOffset,

173

useAnimatedStyle,

174

interpolateColor

175

} from "react-native-reanimated";

176

177

const ScrollOffsetExample = () => {

178

const scrollRef = useAnimatedRef();

179

const scrollOffset = useScrollOffset(scrollRef);

180

181

// Background color changes based on scroll position

182

const backgroundStyle = useAnimatedStyle(() => ({

183

backgroundColor: interpolateColor(

184

scrollOffset.value,

185

[0, 300, 600],

186

["white", "lightblue", "darkblue"]

187

),

188

}));

189

190

return (

191

<Animated.View style={[{ flex: 1 }, backgroundStyle]}>

192

<Animated.ScrollView ref={scrollRef}>

193

{Array.from({ length: 100 }).map((_, index) => (

194

<Animated.View

195

key={index}

196

style={{

197

height: 60,

198

margin: 10,

199

backgroundColor: "rgba(255, 255, 255, 0.8)",

200

borderRadius: 10,

201

justifyContent: "center",

202

alignItems: "center",

203

}}

204

>

205

<Animated.Text>Item {index}</Animated.Text>

206

</Animated.View>

207

))}

208

</Animated.ScrollView>

209

</Animated.View>

210

);

211

};

212

```

213

214

### Animated Reactions

215

216

React to changes in shared values with custom side effects.

217

218

```typescript { .api }

219

/**

220

* Executes side effects in response to shared value changes

221

* @param prepare - Function to prepare/compute values (runs on UI thread)

222

* @param react - Function to handle changes (runs on JS thread)

223

* @param dependencies - Optional dependency array for optimization

224

*/

225

function useAnimatedReaction<T>(

226

prepare: () => T,

227

react: (prepared: T, previous: T | null) => void,

228

dependencies?: React.DependencyList

229

): void;

230

```

231

232

**Usage Examples:**

233

234

```typescript

235

import React, { useState } from "react";

236

import Animated, {

237

useSharedValue,

238

useAnimatedReaction,

239

useAnimatedStyle,

240

withSpring,

241

runOnJS

242

} from "react-native-reanimated";

243

244

const AnimatedReactionExample = () => {

245

const scale = useSharedValue(1);

246

const [message, setMessage] = useState("Normal size");

247

248

// React to scale changes

249

useAnimatedReaction(

250

() => scale.value > 1.5,

251

(isLarge, wasLarge) => {

252

if (isLarge !== wasLarge) {

253

runOnJS(setMessage)(isLarge ? "Large!" : "Normal size");

254

}

255

}

256

);

257

258

// React to multiple values

259

const rotation = useSharedValue(0);

260

const position = useSharedValue({ x: 0, y: 0 });

261

262

useAnimatedReaction(

263

() => ({

264

isRotated: Math.abs(rotation.value) > 45,

265

isOffCenter: Math.abs(position.value.x) > 50 || Math.abs(position.value.y) > 50,

266

}),

267

(current, previous) => {

268

if (current.isRotated && !previous?.isRotated) {

269

runOnJS(console.log)("Started rotating");

270

}

271

if (current.isOffCenter && !previous?.isOffCenter) {

272

runOnJS(console.log)("Moved off center");

273

}

274

}

275

);

276

277

const animatedStyle = useAnimatedStyle(() => ({

278

transform: [

279

{ scale: scale.value },

280

{ rotate: `${rotation.value}deg` },

281

{ translateX: position.value.x },

282

{ translateY: position.value.y },

283

],

284

}));

285

286

const handlePress = () => {

287

scale.value = withSpring(scale.value === 1 ? 2 : 1);

288

rotation.value = withSpring(rotation.value + 90);

289

position.value = withSpring({

290

x: Math.random() * 100 - 50,

291

y: Math.random() * 100 - 50,

292

});

293

};

294

295

return (

296

<>

297

<Animated.Text>{message}</Animated.Text>

298

<Animated.View

299

style={[

300

{

301

width: 100,

302

height: 100,

303

backgroundColor: "lightcoral",

304

borderRadius: 10,

305

},

306

animatedStyle,

307

]}

308

/>

309

<Button title="Animate" onPress={handlePress} />

310

</>

311

);

312

};

313

```

314

315

### Keyboard Handling

316

317

Track keyboard animations and adjust UI accordingly.

318

319

```typescript { .api }

320

/**

321

* Provides animated keyboard information

322

* @param options - Optional configuration for keyboard tracking

323

* @returns Animated keyboard information object

324

*/

325

function useAnimatedKeyboard(options?: AnimatedKeyboardOptions): AnimatedKeyboardInfo;

326

327

interface AnimatedKeyboardInfo {

328

/** Current keyboard height as SharedValue */

329

height: SharedValue<number>;

330

/** Keyboard state as SharedValue */

331

state: SharedValue<KeyboardState>;

332

}

333

334

interface AnimatedKeyboardOptions {

335

/** Whether to use safe area insets */

336

isStatusBarTranslucentAndroid?: boolean;

337

}

338

339

enum KeyboardState {

340

CLOSED = 0,

341

OPEN = 1,

342

CLOSING = 2,

343

OPENING = 3,

344

}

345

```

346

347

**Usage Example:**

348

349

```typescript

350

import React from "react";

351

import { TextInput } from "react-native";

352

import Animated, {

353

useAnimatedKeyboard,

354

useAnimatedStyle,

355

KeyboardState

356

} from "react-native-reanimated";

357

358

const KeyboardExample = () => {

359

const keyboard = useAnimatedKeyboard();

360

361

// Adjust container based on keyboard

362

const containerStyle = useAnimatedStyle(() => ({

363

paddingBottom: keyboard.height.value,

364

backgroundColor: keyboard.state.value === KeyboardState.OPEN ? "lightblue" : "white",

365

}));

366

367

// Animate input field

368

const inputStyle = useAnimatedStyle(() => ({

369

transform: [

370

{

371

translateY: keyboard.state.value === KeyboardState.OPENING ? -20 : 0,

372

},

373

],

374

}));

375

376

return (

377

<Animated.View style={[{ flex: 1, padding: 20 }, containerStyle]}>

378

<Animated.View style={inputStyle}>

379

<TextInput

380

placeholder="Type something..."

381

style={{

382

height: 40,

383

borderWidth: 1,

384

borderColor: "gray",

385

borderRadius: 5,

386

paddingHorizontal: 10,

387

}}

388

/>

389

</Animated.View>

390

</Animated.View>

391

);

392

};

393

```

394

395

### Sensor Handling

396

397

Access device sensors with animated values.

398

399

```typescript { .api }

400

/**

401

* Provides access to device sensors with animated values

402

* @param sensorType - Type of sensor to access

403

* @param config - Optional sensor configuration

404

* @returns Animated sensor data

405

*/

406

function useAnimatedSensor(

407

sensorType: SensorType,

408

config?: SensorConfig

409

): AnimatedSensor;

410

411

enum SensorType {

412

ACCELEROMETER = 1,

413

GYROSCOPE = 2,

414

GRAVITY = 3,

415

MAGNETIC_FIELD = 4,

416

ROTATION = 5,

417

}

418

419

interface SensorConfig {

420

/** Sensor update interval in milliseconds */

421

interval?: number;

422

/** Whether to adjust for device orientation */

423

adjustToInterfaceOrientation?: boolean;

424

/** Interface orientation to adjust for */

425

interfaceOrientation?: InterfaceOrientation;

426

/** iOS reference frame */

427

iosReferenceFrame?: IOSReferenceFrame;

428

}

429

430

interface AnimatedSensor {

431

sensor: SharedValue<Value3D | ValueRotation>;

432

unregister: () => void;

433

}

434

435

interface Value3D {

436

x: number;

437

y: number;

438

z: number;

439

interfaceOrientation: InterfaceOrientation;

440

}

441

442

interface ValueRotation {

443

qw: number;

444

qx: number;

445

qy: number;

446

qz: number;

447

yaw: number;

448

pitch: number;

449

roll: number;

450

interfaceOrientation: InterfaceOrientation;

451

}

452

```

453

454

**Usage Example:**

455

456

```typescript

457

import React, { useEffect } from "react";

458

import Animated, {

459

useAnimatedSensor,

460

useAnimatedStyle,

461

SensorType

462

} from "react-native-reanimated";

463

464

const SensorExample = () => {

465

const accelerometer = useAnimatedSensor(SensorType.ACCELEROMETER, {

466

interval: 100, // Update every 100ms

467

});

468

469

const gyroscope = useAnimatedSensor(SensorType.GYROSCOPE);

470

471

// Tilt effect based on accelerometer

472

const tiltStyle = useAnimatedStyle(() => {

473

const { x, y } = accelerometer.sensor.value;

474

return {

475

transform: [

476

{ rotateX: `${y * 45}deg` },

477

{ rotateY: `${-x * 45}deg` },

478

],

479

};

480

});

481

482

// Rotation based on gyroscope

483

const rotationStyle = useAnimatedStyle(() => {

484

const { z } = gyroscope.sensor.value;

485

return {

486

transform: [{ rotateZ: `${z * 180}deg` }],

487

};

488

});

489

490

useEffect(() => {

491

// Cleanup sensors on unmount

492

return () => {

493

accelerometer.unregister();

494

gyroscope.unregister();

495

};

496

}, []);

497

498

return (

499

<>

500

<Animated.Text>Tilt your device!</Animated.Text>

501

502

<Animated.View

503

style={[

504

{

505

width: 100,

506

height: 100,

507

backgroundColor: "lightgreen",

508

margin: 20,

509

borderRadius: 10,

510

},

511

tiltStyle,

512

]}

513

>

514

<Animated.Text>Tilt</Animated.Text>

515

</Animated.View>

516

517

<Animated.View

518

style={[

519

{

520

width: 100,

521

height: 100,

522

backgroundColor: "lightcoral",

523

margin: 20,

524

borderRadius: 10,

525

},

526

rotationStyle,

527

]}

528

>

529

<Animated.Text>Rotate</Animated.Text>

530

</Animated.View>

531

</>

532

);

533

};

534

```

535

536

### Generic Event Handling

537

538

Handle custom events and gestures with optimized performance.

539

540

```typescript { .api }

541

/**

542

* Creates a generic event handler that can run on the UI thread

543

* @param handler - Event handler function

544

* @param eventNames - Optional array of event names to handle

545

* @returns Processed event handler

546

*/

547

function useEvent<T>(

548

handler: EventHandler<T>,

549

eventNames?: string[]

550

): EventHandlerProcessed<T>;

551

552

type EventHandler<T> = (event: ReanimatedEvent<T>) => void;

553

554

interface ReanimatedEvent<T> {

555

eventName: string;

556

[key: string]: any;

557

}

558

```

559

560

### Accessibility Support

561

562

Check for reduced motion preferences for accessibility.

563

564

```typescript { .api }

565

/**

566

* Returns the current reduced motion preference

567

* @returns Boolean indicating if reduced motion is preferred

568

*/

569

function useReducedMotion(): SharedValue<boolean>;

570

```

571

572

**Usage Example:**

573

574

```typescript

575

import React from "react";

576

import Animated, {

577

useReducedMotion,

578

useSharedValue,

579

useAnimatedStyle,

580

withTiming,

581

withSpring

582

} from "react-native-reanimated";

583

import { Button } from "react-native";

584

585

const AccessibilityExample = () => {

586

const reducedMotion = useReducedMotion();

587

const scale = useSharedValue(1);

588

589

const animatedStyle = useAnimatedStyle(() => ({

590

transform: [{ scale: scale.value }],

591

}));

592

593

const handlePress = () => {

594

// Use different animation based on accessibility preference

595

if (reducedMotion.value) {

596

// Reduced motion: quick, simple animation

597

scale.value = withTiming(scale.value === 1 ? 1.1 : 1, { duration: 200 });

598

} else {

599

// Full motion: bouncy spring animation

600

scale.value = withSpring(scale.value === 1 ? 1.3 : 1);

601

}

602

};

603

604

return (

605

<>

606

<Animated.View

607

style={[

608

{

609

width: 100,

610

height: 100,

611

backgroundColor: "lightblue",

612

borderRadius: 10,

613

},

614

animatedStyle,

615

]}

616

>

617

<Animated.Text>Accessible Animation</Animated.Text>

618

</Animated.View>

619

620

<Button title="Animate" onPress={handlePress} />

621

</>

622

);

623

};

624

```

625

626

### Advanced Event Handling

627

628

Low-level event handling hooks for complex event composition and custom patterns.

629

630

```typescript { .api }

631

/**

632

* Creates a generic event handler for any native event

633

* @param handler - Function to handle the event

634

* @param eventNames - Array of event names to listen for

635

* @param rebuild - Whether to rebuild the handler on changes

636

* @returns Processed event handler

637

*/

638

function useEvent<Event extends object, Context = never>(

639

handler: EventHandler<Event, Context>,

640

eventNames?: readonly string[],

641

rebuild?: boolean

642

): EventHandlerProcessed<Event, Context>;

643

644

/**

645

* Composes multiple event handlers into a single handler

646

* @param handlers - Array of event handlers to compose

647

* @returns Composed event handler

648

*/

649

function useComposedEventHandler<Event extends object, Context = never>(

650

handlers: (EventHandlerProcessed<Event, Context> | null)[]

651

): EventHandlerProcessed<Event, Context>;

652

653

/**

654

* Low-level hook for creating reusable event handler logic

655

* @param handlers - Object of event handlers

656

* @param dependencies - Optional dependency array

657

* @returns Handler context with dependency information

658

*/

659

function useHandler<Event extends object, Context extends Record<string, unknown>>(

660

handlers: Record<string, ((event: ReanimatedEvent<Event>, context: Context) => void) | undefined>,

661

dependencies?: React.DependencyList

662

): UseHandlerContext<Context>;

663

664

type EventHandler<Event extends object, Context = never> =

665

(event: ReanimatedEvent<Event>, context?: Context) => void;

666

```

667

668

**Usage Examples:**

669

670

```typescript

671

// Generic event handling

672

const MyComponent = () => {

673

const handleTouch = useEvent((event) => {

674

'worklet';

675

console.log('Touch at:', event.x, event.y);

676

}, ['onTouchStart', 'onTouchMove']);

677

678

return <Animated.View onTouchStart={handleTouch} onTouchMove={handleTouch} />;

679

};

680

681

// Composing multiple handlers

682

const ComposedExample = () => {

683

const handler1 = useEvent((event) => {

684

'worklet';

685

console.log('Handler 1:', event.contentOffset.y);

686

});

687

688

const handler2 = useEvent((event) => {

689

'worklet';

690

console.log('Handler 2:', event.velocity?.y);

691

});

692

693

const composedHandler = useComposedEventHandler([handler1, handler2]);

694

695

return <Animated.ScrollView onScroll={composedHandler} />;

696

};

697

698

// Advanced handler patterns

699

const AdvancedExample = () => {

700

const { context, doDependenciesDiffer } = useHandler({

701

onScroll: (event, ctx) => {

702

'worklet';

703

ctx.lastScrollY = event.contentOffset.y;

704

},

705

onMomentumEnd: (event, ctx) => {

706

'worklet';

707

console.log('Scroll ended at:', ctx.lastScrollY);

708

},

709

}, []);

710

711

// Use the handler context for complex logic

712

useEffect(() => {

713

if (doDependenciesDiffer) {

714

console.log('Handler dependencies changed');

715

}

716

}, [doDependenciesDiffer]);

717

718

return <Animated.ScrollView /* handler setup */ />;

719

};

720

```

721

722

## Type Definitions

723

724

```typescript { .api }

725

type ScrollHandlerProcessed<T> = (event: ScrollEvent) => void;

726

727

type EventHandlerProcessed<T> = (event: ReanimatedEvent<T>) => void;

728

729

interface UseHandlerContext<T> {

730

context: SharedValue<T>;

731

doDependenciesDiffer: boolean;

732

useWeb: boolean;

733

}

734

735

enum InterfaceOrientation {

736

ROTATION_0 = 0,

737

ROTATION_90 = 90,

738

ROTATION_180 = 180,

739

ROTATION_270 = 270,

740

}

741

742

enum IOSReferenceFrame {

743

XArbitraryZVertical = "XArbitraryZVertical",

744

XArbitraryCorrectedZVertical = "XArbitraryCorrectedZVertical",

745

XMagneticNorthZVertical = "XMagneticNorthZVertical",

746

XTrueNorthZVertical = "XTrueNorthZVertical",

747

}

748

```