Vega App Performance Series, Part 3: Optimizing for Fluidity and Responsiveness

Introduction

In Part 1, we covered the key metrics for measuring fluidity and responsiveness. In Part 2, we identified performance issues in the home screen implementation using the KPI Visualizer and Perfetto traces. Now let’s fix those problems.

This article shows the optimized implementation and explains why it works.

Sample Code: The examples in this series are based on the Vega Developer Workshop for TV Apps repository. The README includes a hands-on workshop you can follow along with if you’d like to experiment with these patterns yourself.


The Optimized Implementation

In Part 2, we identified that the degraded home screen had:

  • 97% fluidity (target: 99%+)
  • 41.4ms max focus response (target: <16ms)
  • 19 instances of 3+ consecutive dropped frames
  • ~50 fps (target: 60fps)

The root cause was blocking work and non-native animations in focus handlers, combined with an unoptimized FlatList.

Optimized HomeScreen with Carousel

Here’s the optimized version using Vega’s Carousel component:

import {Carousel, ItemInfo} from '@amazon-devices/kepler-ui-components';

const HomeScreen = ({navigation}) => {
  const [contentRows, setContentRows] = useState([]);
  // ... data fetching code ...

  return (
    <ScrollView style={styles.container}>
      {contentRows.map((row, rowIndex) => (
        <ContentRowComponent
          key={row.title}
          row={row}
          onItemPress={handleItemPress}
        />
      ))}
    </ScrollView>
  );
};

const ContentRowComponent = ({row, onItemPress}) => {
  const renderItem = ({item, index}) => {
    return (
      <MovieCard
        item={item}
        onPress={() => onItemPress(item)}
      />
    );
  };

  const itemInfo: ItemInfo[] = [
    {
      view: MovieCard,
      dimension: {width: ITEM_WIDTH, height: ITEM_HEIGHT},
    },
  ];

  return (
    <View style={styles.rowContainer}>
      <Text style={styles.rowHeader}>{row.title}</Text>
      <Carousel
        data={row.items}
        orientation="horizontal"
        itemDimensions={itemInfo}
        renderItem={renderItem}
        getItemForIndex={() => MovieCard}
        keyProvider={(item, index) => `${index}-${item.id}`}
        focusIndicatorType="fixed"
      />
    </View>
  );
};

Optimized MovieCard with Simple Focus Handlers

// Memoized to prevent unnecessary re-renders
const MovieCard = React.memo(({item, onPress}) => {
  const [focused, setFocused] = useState(false);

  // GOOD: Minimal focus handler - just updates state
  // No blocking work, no animations
  return (
    <Pressable
      style={[styles.card, focused && styles.cardFocused]}
      onFocus={() => setFocused(true)}
      onBlur={() => setFocused(false)}
      onPress={onPress}>
      <Image
        source={{uri: item.images.thumbnail_450x253}}
        style={styles.thumbnail}
        resizeMode="cover"
      />
    </Pressable>
  );
});

Why This Works

Fluidity Improvements:

Fix Why It Helps
Vega’s Carousel component Native-level performance with built-in recycling and focus management
itemDimensions prop Eliminates dynamic measurement during scroll
React.memo() on MovieCard Prevents re-renders when props haven’t changed
No new object references Stable props don’t trigger unnecessary updates

Responsiveness Improvements:

Fix Why It Helps
No blocking work in handlers Focus events complete in <2ms instead of 50ms
No non-native animations JS thread stays free for input processing
Pre-defined styles No inline style computation on every render
Simple state updates setFocused(true) is fast and non-blocking

Results After Optimization

The improvement is clear after re-testing with the KPI Visualizer and analyzing Perfetto events:

Metric Degraded Optimized Improvement
Max event duration 52.66ms 6.76ms 87% faster
Avg event duration 17.39ms 0.61ms 96% faster
Metric Degraded Optimized Improvement
Fluidity % 97% 100% 3% improvement
Max frame duration 62.94ms 7.54ms 88% faster
Frames over 16ms 6 0 100% fewer

The optimized version achieves 100% fluidity by keeping the JS thread free for rendering and input processing.


Summary

Building fluid and responsive apps for Fire TV requires attention to how your app feels, not just how fast it runs.

Key takeaways from this series:

  1. Use the right components: Vega’s Carousel is optimized for TV; prefer it over FlatList for content rows.

  2. Keep focus handlers simple: Just update visual state. Defer heavy work with InteractionManager.runAfterInteractions().

  3. Use native-driven animations: Always set useNativeDriver: true.

  4. Memoize appropriately: Use React.memo(), useCallback(), and useMemo().

  5. Measure regularly: Run KPI Visualizer tests throughout development.

  6. Use Perfetto for deep analysis: When KPIs indicate problems, traces reveal the root cause.

For comprehensive optimization guidance, see App Performance Best Practices.

Additional Resources

Performance Measurement

Optimization Guidance

Debugging Tools


Last updated: December 2025