# Source: https://stately.ai/docs/cheatsheet.mdx

# Cheatsheet (/docs/cheatsheet)
Use this cheatsheet to quickly look up the syntax for XState v5.

## Installing XState

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate
    ```
  </Tab>
</Tabs>

[Read more on installing XState](installation).

## Creating a state machine

```ts
import { setup, createActor, assign } from 'xstate';

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

const actor = createActor(machine);
actor.subscribe((snapshot) => {
  console.log(snapshot.value);
});

actor.start();
// logs 'active' with context { count: 1 }

actor.send({ type: 'toggle' });
// logs 'inactive' with context { count: 1 }
actor.send({ type: 'toggle' });
// logs 'active' with context { count: 2 }
actor.send({ type: 'toggle' });
// logs 'inactive' with context { count: 2 }
```

[Read more about the actor model](actor-model).

## Creating promise logic

```ts
import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(async () => {
  const response = await fetch('https://dog.ceo/api/breeds/image/random');
  const dog = await response.json();
  return dog;
});

const actor = createActor(promiseLogic);

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

actor.start();
// logs: {
//   message: "https://images.dog.ceo/breeds/kuvasz/n02104029_110.jpg",
//   status: "success"
// }
```

[Read more about promise actor logic](/docs/actors#actors-as-promises).

## Creating transition logic

A transition function is just like a reducer.

```ts
import { fromTransition, createActor } from 'xstate';

const transitionLogic = fromTransition(
  (state, event) => {
    switch (event.type) {
      case 'inc':
        return {
          ...state,
          count: state.count + 1,
        };
      default:
        return state;
    }
  },
  { count: 0 }, // initial state
);

const actor = createActor(transitionLogic);

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

actor.start();
// logs { count: 0 }

actor.send({ type: 'inc' });
// logs { count: 1 }
actor.send({ type: 'inc' });
// logs { count: 2 }
```

[Read more about transition actors](/docs/actors#fromtransition).

## Creating observable logic

```ts
import { fromObservable, createActor } from 'xstate';
import { interval } from 'rxjs';

const observableLogic = fromObservable(() => interval(1000));

const actor = createActor(observableLogic);

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

actor.start();
// logs 0, 1, 2, 3, 4, 5, ...
// every second
```

[Read more about observable actors](/docs/actors#fromobservable).

## Creating callback logic

```ts
import { fromCallback, createActor } from 'xstate';

const callbackLogic = fromCallback(({ sendBack, receive }) => {
  const i = setTimeout(() => {
    sendBack({ type: 'timeout' });
  }, 1000);

  receive((event) => {
    if (event.type === 'cancel') {
      console.log('canceled');
      clearTimeout(i);
    }
  });

  return () => {
    clearTimeout(i);
  };
});

const actor = createActor(callbackLogic);

actor.start();

actor.send({ type: 'cancel' });
// logs 'canceled'
```

[Read more about callback actors](/docs/actors#fromcallback).

## Parent states

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  /* ... */
}).createMachine({
  id: 'parent',
  initial: 'active',
  states: {
    active: {
      initial: 'one',
      states: {
        one: {
          on: {
            NEXT: { target: 'two' },
          },
        },
        two: {},
      },
      on: {
        NEXT: { target: 'inactive' },
      },
    },
    inactive: {},
  },
});

const actor = createActor(machine);

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

actor.start();
// logs { active: 'one' }

actor.send({ type: 'NEXT' });
// logs { active: 'two' }

actor.send({ type: 'NEXT' });
// logs 'inactive'
```

[Read more about parent states](parent-states).

## Actions

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  actions: {
    activate: () => {
      /* ... */
    },
    deactivate: () => {
      /* ... */
    },
    notify: (_, params: { message: string }) => {
      /* ... */
    },
  },
}).createMachine({
  id: 'toggle',
  initial: 'active',
  states: {
    active: {
      // [!code highlight:1]
      entry: { type: 'activate' },
      // [!code highlight:1]
      exit: { type: 'deactivate' },
      on: {
        toggle: {
          target: 'inactive',
          // [!code highlight:1]
          actions: [{ type: 'notify' }],
        },
      },
    },
    inactive: {
      on: {
        toggle: {
          target: 'active',
          // [!code highlight:9]
          actions: [
            // action with params
            {
              type: 'notify',
              params: {
                message: 'Some notification',
              },
            },
          ],
        },
      },
    },
  },
});

const actor = createActor(
  machine.provide({
    actions: {
      notify: (_, params) => {
        console.log(params.message ?? 'Default message');
      },
      activate: () => {
        console.log('Activating');
      },
      deactivate: () => {
        console.log('Deactivating');
      },
    },
  }),
);

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

actor.send({ type: 'toggle' });
// logs 'Deactivating'
// logs 'Default message'

actor.send({ type: 'toggle' });
// logs 'Some notification'
// logs 'Activating'
```

[Read more about actions](actions).

## Guards

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  // [!code highlight:9]
  guards: {
    canBeToggled: ({ context }) => context.canActivate,
    isAfterTime: (_, params) => {
      const { time } = params;
      const [hour, minute] = time.split(':');
      const now = new Date();
      return now.getHours() > hour && now.getMinutes() > minute;
    },
  },
  actions: {
    notifyNotAllowed: () => {
      console.log('Cannot be toggled');
    },
  },
}).createMachine({
  id: 'toggle',
  initial: 'active',
  context: {
    canActivate: false,
  },
  states: {
    inactive: {
      on: {
        toggle: [
          {
            target: 'active',
            // [!code highlight:1]
            guard: 'canBeToggled',
          },
          {
            actions: 'notifyNotAllowed',
          },
        ],
      },
    },
    active: {
      on: {
        toggle: {
          // Guard with params
          // [!code highlight:1]
          guard: { type: 'isAfterTime', params: { time: '16:00' } },
          target: 'inactive',
        },
      },
      // ...
    },
  },
});

const actor = createActor(machine);

actor.start();
// logs 'Cannot be toggled'
```

[Read more about guards](guards).

## Invoking actors

```ts
import { setup, fromPromise, createActor, assign } from 'xstate';

const loadUserLogic = fromPromise(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
  const user = await response.json();
  return user;
});

const machine = setup({
  // [!code highlight:1]
  actors: { loadUserLogic },
}).createMachine({
  id: 'toggle',
  initial: 'loading',
  context: {
    user: undefined,
  },
  states: {
    loading: {
      // [!code highlight:16]
      invoke: {
        id: 'loadUser',
        src: 'loadUserLogic',
        onDone: {
          target: 'doSomethingWithUser',
          actions: assign({
            user: ({ event }) => event.output,
          }),
        },
        onError: {
          target: 'failure',
          actions: ({ event }) => {
            console.log(event.error);
          },
        },
      },
    },
    doSomethingWithUser: {
      // ...
    },
    failure: {
      // ...
    },
  },
});

const actor = createActor(machine);

actor.subscribe((snapshot) => {
  console.log(snapshot.context.user);
});

actor.start();
// eventually logs:
// { id: 1, name: 'Leanne Graham', ... }
```

[Read more about invoking actors](invoke).

## Spawning actors

```ts
import { setup, fromPromise, createActor, assign } from 'xstate';

const loadUserLogic = fromPromise(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
  const user = await response.json();
  return user;
});

const machine = setup({
  actors: {
    loadUserLogic,
  },
}).createMachine({
  context: {
    userRef: undefined,
  },
  on: {
    loadUser: {
      actions: assign({
        // [!code highlight:1]
        userRef: ({ spawn }) => spawn('loadUserLogic'),
      }),
    },
  },
});

const actor = createActor(machine);
actor.subscribe((snapshot) => {
  const { userRef } = snapshot.context;
  console.log(userRef?.getSnapshot());
});
actor.start();

actor.send({ type: 'loadUser' });
// eventually logs:
// { id: 1, name: 'Leanne Graham', ... }
```

[Read more about spawning actors](spawn).

## Input and output

```ts
import { setup, createActor } from 'xstate';

const greetMachine = setup({
  types: {
    context: {} as { message: string },
    input: {} as { name: string },
  },
}).createMachine({
  // [!code highlight:3]
  context: ({ input }) => ({
    message: `Hello, ${input.name}`,
  }),
  entry: ({ context }) => {
    console.log(context.message);
  },
});

const actor = createActor(greetMachine, {
  // [!code highlight:3]
  input: {
    name: 'David',
  },
});

actor.start();
// logs 'Hello, David'
```

[Read more about input](input).

## Invoking actors with input

```ts
import { setup, createActor, fromPromise } from 'xstate';

const loadUserLogic = fromPromise(async ({ input }) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users/${input.id}`,
  );
  const user = await response.json();
  return user;
});

const machine = setup({
  actors: {
    loadUserLogic,
  },
}).createMachine({
  initial: 'loading user',
  states: {
    'loading user': {
      invoke: {
        id: 'loadUser',
        src: 'loadUserLogic',
        // [!code highlight:3]
        input: {
          id: 3,
        },
        onDone: {
          actions: ({ event }) => {
            console.log(event.output);
          },
        },
      },
    },
  },
});

const actor = createActor(machine);

actor.start();
// eventually logs:
// { id: 3, name: 'Clementine Bauch', ... }
```

[Read more about invoking actors with input](input.mdx#invoking-actors-with-input).

## Types

```ts
import { setup, fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
  /* ... */
});

const machine = setup({
  types: {
    context: {} as {
      count: number;
    };
    events: {} as
      | { type: 'inc'; }
      | { type: 'dec' }
      | { type: 'incBy'; amount: number };
    actions: {} as
      | { type: 'notify'; params: { message: string } }
      | { type: 'handleChange' };
    guards: {} as
      | { type: 'canBeToggled' }
      | { type: 'isAfterTime'; params: { time: string } };
    children: {} as {
      promise1: 'someSrc';
      promise2: 'someSrc';
    };
    delays: 'shortTimeout' | 'longTimeout';
    tags: 'tag1' | 'tag2';
    input: number;
    output: string;
  },
  actors: {
    promiseLogic
  }
}).createMachine({
  // ...
});
```