Skip to main content
The and() function creates a higher-order guard that evaluates to true only if all guards passed to it evaluate to true.

Signature

function and<
  TContext extends MachineContext,
  TExpressionEvent extends EventObject,
  TArg extends unknown[]
>(
  guards: readonly [
    ...{
      [K in keyof TArg]: SingleGuardArg<
        TContext,
        TExpressionEvent,
        unknown,
        TArg[K]
      >;
    }
  ]
): GuardPredicate<
  TContext,
  TExpressionEvent,
  unknown,
  NormalizeGuardArgArray<DoNotInfer<TArg>>
>;

Parameters

guards
readonly Guard[]
required
An array of guards to evaluate. Each guard can be:
  • A named guard string (e.g., 'isValid')
  • A guard object with type and optional params
  • An inline guard function

Returns

A guard that returns true if all provided guards evaluate to true, otherwise false.

Usage

Basic Example

import { setup, and } from 'xstate';

const machine = setup({
  guards: {
    hasData: ({ context }) => context.data !== null,
    isValid: ({ context }) => context.isValid
  }
}).createMachine({
  context: {
    data: null as string | null,
    isValid: false
  },
  on: {
    SUBMIT: {
      guard: and(['hasData', 'isValid']),
      actions: () => {
        console.log('Both guards passed!');
      }
    }
  }
});

Mixing Named and Inline Guards

import { setup, and } from 'xstate';

const machine = setup({
  guards: {
    isAuthenticated: ({ context }) => context.user !== null
  }
}).createMachine({
  context: {
    user: null as { role: string } | null,
    age: 0
  },
  on: {
    ACCESS: {
      guard: and([
        'isAuthenticated',
        ({ context }) => context.age >= 18,
        ({ context }) => context.user?.role === 'admin'
      ]),
      target: 'adminPanel'
    }
  }
});

With Guard Parameters

import { setup, and } from 'xstate';

const machine = setup({
  guards: {
    hasMinValue: ({ context }, params: { min: number }) => {
      return context.value >= params.min;
    },
    hasMaxValue: ({ context }, params: { max: number }) => {
      return context.value <= params.max;
    }
  }
}).createMachine({
  context: { value: 50 },
  on: {
    VALIDATE: {
      guard: and([
        { type: 'hasMinValue', params: { min: 0 } },
        { type: 'hasMaxValue', params: { max: 100 } }
      ]),
      target: 'valid'
    }
  }
});

Nested Composition

import { setup, and, or } from 'xstate';

const machine = setup({
  guards: {
    isAdmin: ({ context }) => context.role === 'admin',
    isModerator: ({ context }) => context.role === 'moderator',
    hasPermission: ({ context }) => context.hasPermission
  }
}).createMachine({
  on: {
    DELETE: {
      guard: and([
        or(['isAdmin', 'isModerator']),
        'hasPermission'
      ]),
      actions: 'deleteItem'
    }
  }
});

Behavior

  • Short-circuit evaluation: If any guard returns false, remaining guards are not evaluated
  • Empty array: An empty array is treated as true (all zero guards passed)
  • Order matters: Guards are evaluated left-to-right until one returns false

Type Safety

The and() function maintains type safety across all guards:
type Context = { count: number; isValid: boolean };
type Event = { type: 'CHECK'; value: number };

const machine = setup({
  types: {} as {
    context: Context;
    events: Event;
  },
  guards: {
    hasCount: ({ context }) => context.count > 0,
    isValid: ({ context }) => context.isValid
  }
}).createMachine({
  on: {
    CHECK: {
      guard: and([
        'hasCount',
        'isValid',
        ({ context, event }) => context.count > event.value
      ])
    }
  }
});

See Also