Command Palette

Search for a command to run...

Understanding State Machines

Explains how state machines work in the dotLottie React Native Player. Covers the core model, workflow, controller methods, setting inputs, event callbacks, and interactive examples.

Understanding State Machines

This page explains how state machines work in the React Native player — what they are, how they integrate with animation playback, and when to use them. It also documents the controller methods for loading, starting, and interacting with state machines.

Note: State machine support is an actively developing feature. APIs and capabilities might change. If your state machine isn't working as expected please create an issue on Github↗.

Core Workflow

  1. Define: Create your state machine logic (states, transitions, events, context) within your .lottie file.

  2. Load Animation: Set up the DotLottie component with your animation source.

  3. Load State Machine: Specify the stateMachineId prop or load programmatically via the controller ref.

  4. Start State Machine: The state machine starts automatically when using stateMachineId, or call stateMachineStart() after programmatic loading.

  5. Interact: Send inputs and fire events from your application code using the controller ref.

  6. Listen (Optional): Use event callback props to react to state changes, transitions, and input changes.

  7. Stop (Optional): Stop the state machine using the controller when interactivity is no longer needed.

Basic Setup

The simplest way to use a state machine is to set the stateMachineId prop and attach a ref to access the controller:

import React, { useRef, useState } from 'react';
import { View, Text } from 'react-native';
import { DotLottie, type Dotlottie } from '@lottiefiles/dotlottie-react-native';

function StateMachineExample() {
  const controllerRef = useRef<Dotlottie>(null);
  const [currentState, setCurrentState] = useState('idle');

  return (
    <View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
      <DotLottie
        ref={controllerRef}
        source={{ uri: 'https://lottie.host/your-animation.lottie' }}
        style={{ width: 300, height: 300 }}
        stateMachineId="myStateMachine"
        onStateMachineStateEntered={(state) => {
          setCurrentState(state);
        }}
      />
      <Text>Current State: {currentState}</Text>
    </View>
  );
}

Setting Inputs

State machines can use boolean, numeric, and string inputs to control transitions. Set inputs via the controller ref.

Boolean Inputs

controllerRef.current?.stateMachineSetBooleanInput('isActive', true);
controllerRef.current?.stateMachineSetBooleanInput('isActive', false);

Numeric Inputs

controllerRef.current?.stateMachineSetNumericInput('speed', 5.0);

String Inputs

controllerRef.current?.stateMachineSetStringInput('theme', 'dark');

Interactions and Events

Pointer and click interactions defined in the state machine's interactions array (e.g., Click, PointerDown, PointerEnter) are handled automatically by the React Native player based on user touches on the DotLottie component. You do not need to wire up gesture handlers for these.

Use stateMachineFire to trigger Event-type inputs explicitly from your application code — for example, responding to a button press or other app-level gesture outside the animation component:

Firing Events

Events cause immediate transitions in the state machine:

// Fire a named event
controllerRef.current?.stateMachineFire('onTap');

Controller State Machine Methods

The following methods are available on the controller ref for state machine interaction:

MethodDescription
stateMachineLoad(stateMachineId: string)Loads a state machine by ID.
stateMachineStart()Starts the loaded state machine.
stateMachineStop()Stops the state machine.
stateMachineFire(event: string)Fires a named event to the state machine.
stateMachineSetBooleanInput(key: string, value: boolean)Sets a boolean input.
stateMachineSetNumericInput(key: string, value: number)Sets a numeric input.
stateMachineSetStringInput(key: string, value: string)Sets a string input.

State Machine Event Callbacks

Listen to state machine events using callback props on the DotLottie component:

<DotLottie
  source={require('./animation.lottie')}
  style={{ width: 300, height: 300 }}
  stateMachineId="myStateMachine"

  // Lifecycle
  onStateMachineStart={() => {
    console.log('State machine started');
  }}
  onStateMachineStop={() => {
    console.log('State machine stopped');
  }}

  // State changes
  onStateMachineStateEntered={(state) => {
    console.log('Entered state:', state);
  }}
  onStateMachineStateExit={(state) => {
    console.log('Exited state:', state);
  }}
  onStateMachineTransition={(from, to) => {
    console.log(`Transition: ${from} → ${to}`);
  }}

  // Input changes
  onStateMachineBooleanInputChange={(name, oldVal, newVal) => {
    console.log(`${name}: ${oldVal} → ${newVal}`);
  }}
  onStateMachineNumericInputChange={(name, oldVal, newVal) => {
    console.log(`${name}: ${oldVal} → ${newVal}`);
  }}
  onStateMachineStringInputChange={(name, oldVal, newVal) => {
    console.log(`${name}: ${oldVal} → ${newVal}`);
  }}

  // Events and errors
  onStateMachineInputFired={(inputName) => {
    console.log('Input fired:', inputName);
  }}
  onStateMachineError={(error) => {
    console.log('Error:', error);
  }}
  onStateMachineCustomEvent={(event) => {
    console.log('Custom event:', event);
  }}
/>

Complete Interactive Example

Here's a complete example of an interactive button with multiple states:

import React, { useRef, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { DotLottie, type Dotlottie } from '@lottiefiles/dotlottie-react-native';

function InteractiveButton() {
  const controllerRef = useRef<Dotlottie>(null);
  const [currentState, setCurrentState] = useState('idle');
  const [isEnabled, setIsEnabled] = useState(true);

  const handlePressIn = () => {
    if (isEnabled) {
      controllerRef.current?.stateMachineFire('pressDown');
    }
  };

  const handlePressOut = () => {
    if (isEnabled) {
      controllerRef.current?.stateMachineFire('pressUp');
    }
  };

  const toggleEnabled = () => {
    const newValue = !isEnabled;
    setIsEnabled(newValue);
    controllerRef.current?.stateMachineSetBooleanInput('isEnabled', newValue);
  };

  const handleReset = () => {
    controllerRef.current?.stateMachineFire('reset');
  };

  return (
    <View style={styles.container}>
      <TouchableOpacity
        onPressIn={handlePressIn}
        onPressOut={handlePressOut}
        activeOpacity={1}
      >
        <DotLottie
          ref={controllerRef}
          source={require('./button-animation.lottie')}
          style={{ width: 200, height: 200 }}
          stateMachineId="buttonStateMachine"
          onStateMachineStateEntered={(state) => {
            setCurrentState(state);
          }}
        />
      </TouchableOpacity>

      <Text style={styles.stateText}>State: {currentState}</Text>

      <View style={styles.controls}>
        <TouchableOpacity style={styles.button} onPress={toggleEnabled}>
          <Text>{isEnabled ? 'Disable' : 'Enable'}</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={handleReset}>
          <Text>Reset</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  stateText: { fontSize: 18, marginVertical: 20 },
  controls: { flexDirection: 'row', gap: 10 },
  button: { paddingHorizontal: 20, paddingVertical: 10, backgroundColor: '#e0e0e0', borderRadius: 8 },
});

Loading State Machines Programmatically

You can load state machines programmatically instead of using the stateMachineId prop:

// Load by ID after animation is ready
controllerRef.current?.stateMachineLoad('myStateMachine');
controllerRef.current?.stateMachineStart();

// Stop when done
controllerRef.current?.stateMachineStop();

Important: Ensure the animation is loaded before calling stateMachineLoad. You can use the onLoad callback to confirm the animation is ready.

Understanding State Machine Components (Defined in .lottie)

Understanding the structure defined within the .lottie file helps in using the player API effectively:

  • Descriptor: Contains the state machine's unique id (used in stateMachineLoad or the stateMachineId prop) and the index of the initial state.

  • States: Defines possible animation states. The main type PlaybackState controls animation aspects like marker, segment, loop, autoplay, speed, and mode.

  • Transitions: Rules for moving between states (from_state, to_state) based on a trigger event.

  • Events: Triggers for transitions, fired via the stateMachineFire method or by setting inputs.

  • Guards (Optional): Conditions within transitions that check context variables to allow or prevent the transition.

  • Context Variables (Optional): Stored data (Numeric, String, Boolean) used by guards.

Debugging and Troubleshooting

  1. State Machine Not Loading/Starting:

    • Check ID: Verify the stateMachineId matches the ID in the .lottie manifest.

    • Animation Loaded? Ensure the animation loaded before programmatic loading. Use the onLoad callback to confirm.

  2. Events Not Triggering Transitions:

    • Check Event Name: Ensure you're firing the correct event name that matches the state machine definition.

    • Verify Guards: Check guard conditions defined in the .lottie file.

    • State Machine Started? Confirm the state machine is running.

  3. Animation Playback Issues in States:

    • Check Marker/Segment: Ensure marker names or segment frame numbers defined in states exist in the animation.

  4. Debugging Tip: Use onStateMachineStateEntered, onStateMachineStateExit, and onStateMachineTransition callbacks to log state changes.

Last updated: April 10, 2026 at 9:12 AMEdit this page