Convert Figma logo to code with AI

statelyai logoxstate

Actor-based state management & orchestration for complex app logic.

26,868
1,232
26,868
128

Top Related Projects

26,868

Actor-based state management & orchestration for complex app logic.

60,792

A JS library for predictable global state management

27,458

Simple, scalable state management.

33,863

Build forms in React, without the tears 😭

41,619

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.

Quick Overview

XState is a JavaScript and TypeScript library for creating finite state machines and statecharts. It provides a powerful and expressive way to model complex application logic, making it easier to manage and reason about application states and transitions.

Pros

  • Helps manage complex application logic with a clear, visual representation
  • Provides type safety and autocompletion when used with TypeScript
  • Supports both synchronous and asynchronous state transitions
  • Integrates well with popular frameworks like React, Vue, and Svelte

Cons

  • Steep learning curve for developers unfamiliar with state machines and statecharts
  • Can be overkill for simple applications or components
  • Requires additional tooling for optimal visualization and debugging
  • May increase bundle size for smaller projects

Code Examples

  1. Creating a simple toggle machine:
import { createMachine, interpret } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { TOGGLE: 'active' } },
    active: { on: { TOGGLE: 'inactive' } }
  }
});

const toggleService = interpret(toggleMachine).start();
toggleService.send('TOGGLE'); // Transitions to 'active' state
  1. Using guards to conditionally transition:
const lightMachine = createMachine({
  id: 'light',
  initial: 'green',
  context: { timer: 0 },
  states: {
    green: {
      on: {
        TIMER: [
          { target: 'yellow', cond: (context) => context.timer >= 30 }
        ]
      }
    },
    yellow: {
      on: { TIMER: 'red' }
    },
    red: {
      on: { TIMER: 'green' }
    }
  }
});
  1. Integrating with React using the useMachine hook:
import { useMachine } from '@xstate/react';

function ToggleComponent() {
  const [state, send] = useMachine(toggleMachine);

  return (
    <button onClick={() => send('TOGGLE')}>
      {state.value === 'inactive' ? 'Turn On' : 'Turn Off'}
    </button>
  );
}

Getting Started

To start using XState in your project:

  1. Install XState:

    npm install xstate
    
  2. Import and create a machine:

    import { createMachine, interpret } from 'xstate';
    
    const machine = createMachine({
      id: 'example',
      initial: 'idle',
      states: {
        idle: { on: { START: 'running' } },
        running: { on: { STOP: 'idle' } }
      }
    });
    
    const service = interpret(machine).start();
    service.send('START');
    console.log(service.state.value); // 'running'
    
  3. For React integration, install the React package:

    npm install @xstate/react
    

Competitor Comparisons

26,868

Actor-based state management & orchestration for complex app logic.

Pros of XState

  • Robust state management library with support for complex state machines
  • Extensive documentation and active community support
  • Visual editor (XState Viz) for designing and debugging state machines

Cons of XState

  • Steeper learning curve for developers new to state machines
  • Can be overkill for simple state management needs
  • Larger bundle size compared to simpler state management solutions

Code Comparison

XState:

import { createMachine, interpret } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { TOGGLE: 'active' } },
    active: { on: { TOGGLE: 'inactive' } }
  }
});

const toggleService = interpret(toggleMachine).start();
toggleService.send('TOGGLE');

Both repositories appear to be the same project, so there isn't a distinct code comparison to make. The XState library provides a powerful and flexible approach to state management using finite state machines and statecharts.

60,792

A JS library for predictable global state management

Pros of Redux

  • Simpler learning curve for developers familiar with JavaScript
  • Extensive ecosystem with many middleware options and tools
  • Better suited for large-scale applications with complex data flows

Cons of Redux

  • Requires more boilerplate code for setup and state management
  • Can lead to overly complex state structures in smaller applications
  • Less intuitive for handling asynchronous operations without additional middleware

Code Comparison

Redux:

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

XState:

const counterMachine = createMachine({
  initial: 'active',
  context: { count: 0 },
  states: {
    active: {
      on: {
        INCREMENT: { actions: assign({ count: (ctx) => ctx.count + 1 }) },
        DECREMENT: { actions: assign({ count: (ctx) => ctx.count - 1 }) }
      }
    }
  }
})

XState provides a more structured approach to state management using finite state machines, which can be beneficial for complex workflows and UI interactions. It offers better visualization tools and can handle side effects more elegantly. However, it may have a steeper learning curve for developers unfamiliar with state machine concepts.

27,458

Simple, scalable state management.

Pros of MobX

  • Simpler learning curve and less boilerplate code
  • More flexible and less opinionated about state structure
  • Better performance for large-scale applications with complex state

Cons of MobX

  • Less predictable state changes due to mutable state
  • Harder to debug and trace state changes
  • Lack of built-in support for complex state machines and transitions

Code Comparison

MobX:

import { makeObservable, observable, action } from "mobx";

class Counter {
  count = 0;
  constructor() {
    makeObservable(this, {
      count: observable,
      increment: action
    });
  }
  increment() {
    this.count++;
  }
}

XState:

import { createMachine, interpret } from "xstate";

const counterMachine = createMachine({
  id: "counter",
  initial: "idle",
  context: { count: 0 },
  states: {
    idle: {
      on: {
        INCREMENT: {
          actions: (context) => context.count++
        }
      }
    }
  }
});

Both MobX and XState are popular state management libraries, but they have different approaches. MobX focuses on reactive programming and mutable state, while XState emphasizes state machines and declarative state transitions. The choice between them depends on the specific needs of your project and your preferred programming paradigm.

33,863

Build forms in React, without the tears 😭

Pros of Formik

  • Specifically designed for form handling, making it more intuitive for form-related tasks
  • Lightweight and focused, resulting in a smaller bundle size
  • Easier learning curve for developers familiar with React forms

Cons of Formik

  • Limited to form management, lacking broader state management capabilities
  • Less flexible for complex, multi-step form scenarios
  • May require additional libraries for advanced form validation

Code Comparison

Formik:

<Formik
  initialValues={{ email: '', password: '' }}
  onSubmit={(values) => console.log(values)}
>
  {({ handleSubmit }) => (
    <form onSubmit={handleSubmit}>
      <Field name="email" type="email" />
      <Field name="password" type="password" />
      <button type="submit">Submit</button>
    </form>
  )}
</Formik>

XState:

const formMachine = createMachine({
  initial: 'idle',
  states: {
    idle: { on: { SUBMIT: 'submitting' } },
    submitting: {
      invoke: {
        src: 'submitForm',
        onDone: 'success',
        onError: 'error'
      }
    },
    success: {},
    error: {}
  }
});

XState offers a more comprehensive state management solution, suitable for complex applications beyond form handling. It provides a clear visualization of application states and transitions, making it easier to manage intricate workflows. However, it may have a steeper learning curve and could be considered overkill for simpler form scenarios where Formik excels.

41,619

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.

Pros of TanStack Query

  • Specialized for data fetching and caching, offering optimized performance for API interactions
  • Automatic background refetching and stale-while-revalidate functionality
  • Simpler API for common data fetching scenarios

Cons of TanStack Query

  • Limited to data management, not suitable for complex application state
  • Steeper learning curve for advanced features and configurations
  • Less flexibility for custom state transitions and side effects

Code Comparison

XState:

const fetchMachine = createMachine({
  initial: 'idle',
  states: {
    idle: { on: { FETCH: 'loading' } },
    loading: { invoke: { src: fetchData, onDone: 'success', onError: 'failure' } },
    success: {},
    failure: {}
  }
});

TanStack Query:

const { data, isLoading, error } = useQuery('userData', fetchUserData);

XState provides a more explicit state machine approach, while TanStack Query offers a simpler API for common data fetching scenarios. XState is more versatile for complex state management, whereas TanStack Query excels in data fetching and caching specifically.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README


XState logotype
Actor-based state management & orchestration for complex app logic. → Documentation

XState is a state management and orchestration solution for JavaScript and TypeScript apps. It has zero dependencies, and is useful for frontend and backend application logic.

It uses event-driven programming, state machines, statecharts, and the actor model to handle complex logic in predictable, robust, and visual ways. XState provides a powerful and flexible way to manage application and workflow state by allowing developers to model logic as actors and state machines.

✨ Create state machines visually in Stately Studio → state.new


📖 Read the documentation

➡️ Create state machines with the Stately Editor

🖥 Download our VS Code extension

📑 Inspired by the SCXML specification

💬 Chat on the Stately Discord Community

✍️ Browse through the many XState examples

Templates

Get started by forking one of these templates on CodeSandbox:

Template

🤖 XState Template (CodeSandbox)

Open in StackBlitz

  • XState v5
  • TypeScript
  • No framework

⚛️ XState + React Template (CodeSandbox)

Open in StackBlitz

  • React
  • XState v5
  • TypeScript

💚 XState + Vue Template (CodeSandbox)

Open in StackBlitz

  • Vue
  • XState v5
  • TypeScript

🧡 XState + Svelte Template (CodeSandbox)

Open in StackBlitz

Super quick start

npm install xstate
import { createMachine, createActor, assign } from 'xstate';

// State machine
const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  context: {
    count: 0
  },
  states: {
    inactive: {
      on: {
        TOGGLE: { target: 'active' }
      }
    },
    active: {
      entry: assign({ count: ({ context }) => context.count + 1 }),
      on: {
        TOGGLE: { target: 'inactive' }
      }
    }
  }
});

// Actor (instance of the machine logic, like a store)
const toggleActor = createActor(toggleMachine);
toggleActor.subscribe((state) => console.log(state.value, state.context));
toggleActor.start();
// => logs 'inactive', { count: 0 }

toggleActor.send({ type: 'TOGGLE' });
// => logs 'active', { count: 1 }

toggleActor.send({ type: 'TOGGLE' });
// => logs 'inactive', { count: 1 }

Stately Studio

  • Visually create, edit, and collaborate on state machines
  • Export to many formats, including XState v5
  • Test path & documentation autogeneration
  • Deploy to Stately Sky
  • Generate & modify machines with Stately AI
XState Viz

state.new

Why?

Statecharts are a formalism for modeling stateful, reactive systems. This is useful for declaratively describing the behavior of your application, from the individual components to the overall application logic.

Read 📽 the slides (🎥 video) or check out these resources for learning about the importance of finite state machines and statecharts in user interfaces:

Packages

PackageDescription
🤖 xstateCore finite state machine and statecharts library + interpreter
📉 @xstate/graphGraph traversal and model-based testing utilities using XState
⚛️ @xstate/reactReact hooks and utilities for using XState in React applications
💚 @xstate/vueVue composition functions and utilities for using XState in Vue applications
🎷 @xstate/svelteSvelte utilities for using XState in Svelte applications
🥏 @xstate/solidSolid hooks and utilities for using XState in Solid applications
🔍 @statelyai/inspectInspection utilities for XState

Finite State Machines

CodeStatechart
import { createMachine, createActor } from 'xstate';

const lightMachine = createMachine({
  id: 'light',
  initial: 'green',
  states: {
    green: {
      on: {
        TIMER: 'yellow'
      }
    },
    yellow: {
      on: {
        TIMER: 'red'
      }
    },
    red: {
      on: {
        TIMER: 'green'
      }
    }
  }
});

const actor = createActor(lightMachine);

actor.subscribe((state) => {
  console.log(state.value);
});

actor.start();
// logs 'green'

actor.send({ type: 'TIMER' });
// logs 'yellow'
CodeStatechart
Finite states
Open in Stately Studio

Hierarchical (Nested) State Machines

CodeStatechart
import { createMachine, createActor } from 'xstate';

const pedestrianStates = {
  initial: 'walk',
  states: {
    walk: {
      on: {
        PED_TIMER: 'wait'
      }
    },
    wait: {
      on: {
        PED_TIMER: 'stop'
      }
    },
    stop: {}
  }
};

const lightMachine = createMachine({
  id: 'light',
  initial: 'green',
  states: {
    green: {
      on: {
        TIMER: 'yellow'
      }
    },
    yellow: {
      on: {
        TIMER: 'red'
      }
    },
    red: {
      on: {
        TIMER: 'green'
      },
      ...pedestrianStates
    }
  }
});

const actor = createActor(lightMachine);

actor.subscribe((state) => {
  console.log(state.value);
});

actor.start();
// logs 'green'

actor.send({ type: 'TIMER' });
// logs 'yellow'

actor.send({ type: 'TIMER' });
// logs { red: 'walk' }

actor.send({ type: 'PED_TIMER' });
// logs { red: 'wait' }
Hierarchical states
Open in Stately Studio

Parallel State Machines

CodeStatechart
import { createMachine, createActor } from 'xstate';

const wordMachine = createMachine({
  id: 'word',
  type: 'parallel',
  states: {
    bold: {
      initial: 'off',
      states: {
        on: {
          on: { TOGGLE_BOLD: 'off' }
        },
        off: {
          on: { TOGGLE_BOLD: 'on' }
        }
      }
    },
    underline: {
      initial: 'off',
      states: {
        on: {
          on: { TOGGLE_UNDERLINE: 'off' }
        },
        off: {
          on: { TOGGLE_UNDERLINE: 'on' }
        }
      }
    },
    italics: {
      initial: 'off',
      states: {
        on: {
          on: { TOGGLE_ITALICS: 'off' }
        },
        off: {
          on: { TOGGLE_ITALICS: 'on' }
        }
      }
    },
    list: {
      initial: 'none',
      states: {
        none: {
          on: {
            BULLETS: 'bullets',
            NUMBERS: 'numbers'
          }
        },
        bullets: {
          on: {
            NONE: 'none',
            NUMBERS: 'numbers'
          }
        },
        numbers: {
          on: {
            BULLETS: 'bullets',
            NONE: 'none'
          }
        }
      }
    }
  }
});

const actor = createActor(wordMachine);

actor.subscribe((state) => {
  console.log(state.value);
});

actor.start();
// logs {
//   bold: 'off',
//   italics: 'off',
//   underline: 'off',
//   list: 'none'
// }

actor.send({ type: 'TOGGLE_BOLD' });
// logs {
//   bold: 'on',
//   italics: 'off',
//   underline: 'off',
//   list: 'none'
// }

actor.send({ type: 'TOGGLE_ITALICS' });
// logs {
//   bold: 'on',
//   italics: 'on',
//   underline: 'off',
//   list: 'none'
// }
Parallel states
Open in Stately Studio

History States

CodeStatechart
import { createMachine, createActor } from 'xstate';

const paymentMachine = createMachine({
  id: 'payment',
  initial: 'method',
  states: {
    method: {
      initial: 'cash',
      states: {
        cash: {
          on: {
            SWITCH_CHECK: 'check'
          }
        },
        check: {
          on: {
            SWITCH_CASH: 'cash'
          }
        },
        hist: { type: 'history' }
      },
      on: { NEXT: 'review' }
    },
    review: {
      on: { PREVIOUS: 'method.hist' }
    }
  }
});

const actor = createActor(paymentMachine);

actor.subscribe((state) => {
  console.log(state.value);
});

actor.start();
// logs {
//   value: { method: 'cash' },
// }

actor.send({ type: 'SWITCH_CHECK' });
// logs {
//   value: { method: 'check' },
// }

actor.send({ type: 'NEXT' });
// logs {
//   value: 'review',
// }

actor.send({ type: 'PREVIOUS' });
// logs {
//   value: { method: 'check' },
// }
History state
Open in Stately Studio

Sponsors

Special thanks to the sponsors who support this open-source project:

Transloadit Logo

SemVer Policy

We understand the importance of the public contract and do not intend to release any breaking changes to the runtime API in a minor or patch release. We consider this with any changes we make to the XState libraries and aim to minimize their effects on existing users.

Breaking changes

XState executes much of the user logic itself. Therefore, almost any change to its behavior might be considered a breaking change. We recognize this as a potential problem but believe that treating every change as a breaking change is not practical. We do our best to implement new features thoughtfully to enable our users to implement their logic in a better, safer way.

Any change could affect how existing XState machines behave if those machines are using particular configurations. We do not introduce behavior changes on a whim and aim to avoid making changes that affect most existing machines. But we reserve the right to make some behavior changes in minor releases. Our best judgment of the situation will always dictate such changes. Please always read our release notes before deciding to upgrade.

TypeScript changes

We also reserve a similar right to adjust declared TypeScript definitions or drop support for older versions of TypeScript in a minor release. The TypeScript language itself evolves quickly and often introduces breaking changes in its minor releases. Our team is also continuously learning how to leverage TypeScript more effectively - and the types improve as a result.

For these reasons, it is impractical for our team to be bound by decisions taken when an older version of TypeScript was its latest version or when we didn’t know how to declare our types in a better way. We won’t introduce declaration changes often - but we are more likely to do so than with runtime changes.

Packages

Most of the packages in the XState family declare a peer dependency on XState itself. We’ll be cautious about maintaining compatibility with already-released packages when releasing a new version of XState, but each release of packages depending on XState will always adjust the declared peer dependency range to include the latest version of XState. For example, you should always be able to update xstate without @xstate/react. But when you update @xstate/react, we highly recommend updating xstate too.

NPM DownloadsLast 30 Days