Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Threads in React Native

Threads in React Native

~9 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

Unlike a pure web app, a React Native app runs distributed across MULTIPLE threads – understanding which code runs on which thread also explains why some animations stutter and others don't (see the "Fade in/out Animation" chapter).

1. Description

The classic (bridge) architecture distinguishes three main threads: the JS THREAD runs your React/JavaScript code (a SINGLE thread, no multithreading within JS itself!), the NATIVE/UI THREAD draws the actual native views (Android: main thread, iOS: main thread), the SHADOW THREAD computes the flexbox layout (Yoga engine) in the background. The newer JSI architecture (see the "Bridging" chapter) blurs this separation somewhat, but the basic principle remains.

2. Short example

// Blocks the JS thread for 3 seconds -
// all touch events AND state updates are delayed during this time:
function blockJsThread() {
  const end = Date.now() + 3000;
  while (Date.now() < end) {} // deliberately bad example!
}

3. Complete project: a responsiveness demo

npx create-expo-app threads-demo
cd threads-demo
App.js
import { useRef, useState } from 'react';
import { View, Text, Button, Animated, StyleSheet } from 'react-native';

export default function App() {
  const [count, setCount] = useState(0);
  const rotation = useRef(new Animated.Value(0)).current;

  // Keeps running as long as the component exists - on the NATIVE thread thanks to useNativeDriver
  function startRotation() {
    rotation.setValue(0);
    Animated.loop(
      Animated.timing(rotation, {
        toValue: 1,
        duration: 1500,
        useNativeDriver: true,
      })
    ).start();
  }

  // Deliberately blocks the JS thread
  function blockJsThread() {
    const end = Date.now() + 2000;
    while (Date.now() < end) {}
  }

  const rotationStyle = {
    transform: [{
      rotate: rotation.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] }),
    }],
  };

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.box, rotationStyle]} />
      <Text style={styles.countText}>Count: {count}</Text>
      <Button title="Start rotation (native animation)" onPress={startRotation} />
      <View style={styles.spacing}>
        <Button title="Increment count (JS thread)" onPress={() => setCount((c) => c + 1)} />
      </View>
      <View style={styles.spacing}>
        <Button title="Block JS thread for 2s" onPress={blockJsThread} color="#dc2626" />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', paddingTop: 80 },
  box: { width: 60, height: 60, backgroundColor: '#2563eb', marginBottom: 24 },
  countText: { fontSize: 18, marginBottom: 24 },
  spacing: { marginTop: 12 },
});

4. Explanation

  • "Start rotation" uses useNativeDriver: true – the animation runs entirely on the NATIVE thread; it keeps spinning even while "Block JS thread for 2s" is pressed.
  • "Increment count", on the other hand, runs on the JS thread – during the 2-second block, the button does NOT respond, because the JS event loop is blocked and the onPress event can only be processed afterward.
  • This exact difference explains why useNativeDriver: true matters so much: animations should stay smooth even while the JS thread is busy with expensive work (network parsing, complex state updates).
  • For GENUINELY compute-heavy work (image processing, complex algorithms), there are additional libraries like react-native-worklets-core that run code on their own, separate thread – beyond the scope of this topic.

5. Outputs

Ausgabe
A blue square at the top, below it "Count: 0" and three buttons. After "Start rotation", the square spins continuously. Tapping "Increment count" counts up normally. Tapping "Block JS thread for 2s" freezes EVERY response to button presses for 2 seconds, while the square VISIBLY keeps spinning the whole time.