Actor-based state management & orchestration for complex app logic.
Guard functions and combinators for conditional logic in state machine transitions and actions. Guards provide type-safe predicate functions that determine whether transitions should be taken or actions should be executed.
Core guard predicate functions for evaluating conditions based on context and events.
/**
* Function type for guard predicates that evaluate conditions
* @param args - Guard arguments containing context and event
* @param params - Optional parameters passed to the guard
* @returns Boolean indicating whether the guard condition is met
*/
type GuardPredicate<
TContext,
TExpressionEvent extends EventObject,
TParams = undefined,
TGuard = any
> = {
(args: GuardArgs<TContext, TExpressionEvent>, params: TParams): boolean;
_out_TGuard?: TGuard;
};
interface GuardArgs<TContext, TExpressionEvent extends EventObject> {
/** Current machine context */
context: TContext;
/** Current event being processed */
event: TExpressionEvent;
}
type Guard<TContext, TExpressionEvent extends EventObject, TParams, TGuard> =
| GuardPredicate<TContext, TExpressionEvent, TParams, TGuard>
| { type: string; params?: TParams }
| string;
type UnknownGuard = Guard<any, any, any, any>;Logical operators for combining multiple guards into complex conditional logic.
/**
* Logical AND guard combinator - evaluates to true if all guards evaluate to true
* @param guards - Array of guards to combine with AND logic
* @returns GuardPredicate that returns true when all guards are true
*/
function and<TContext, TExpressionEvent extends EventObject>(
guards: Guard<TContext, TExpressionEvent, any, any>[]
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;
/**
* Logical OR guard combinator - evaluates to true if any guard evaluates to true
* @param guards - Array of guards to combine with OR logic
* @returns GuardPredicate that returns true when any guard is true
*/
function or<TContext, TExpressionEvent extends EventObject>(
guards: Guard<TContext, TExpressionEvent, any, any>[]
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;
/**
* Logical NOT guard combinator - evaluates to true if the guard evaluates to false
* @param guard - Guard to negate
* @returns GuardPredicate that returns the opposite of the input guard
*/
function not<TContext, TExpressionEvent extends EventObject>(
guard: Guard<TContext, TExpressionEvent, any, any>
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;Usage Examples:
import { and, or, not } from "xstate/guards";
// Combine guards with AND logic
and([
({ context }) => context.isLoggedIn,
({ context }) => context.hasPermission,
({ event }) => event.type === "SUBMIT"
])
// Combine guards with OR logic
or([
({ context }) => context.isAdmin,
({ context }) => context.isOwner,
({ event }) => event.force === true
])
// Negate a guard
not(({ context }) => context.isDisabled)
// Complex combinations
and([
({ context }) => context.user != null,
or([
({ context }) => context.user.role === "admin",
({ context }) => context.user.role === "moderator"
]),
not(({ context }) => context.user.suspended)
])Guard for checking if the machine is currently in specific states.
/**
* Guard to check if machine is in specific state(s)
* @param stateValue - State value to check (string, object, or state ID)
* @returns GuardPredicate that returns true when machine matches the state
*/
function stateIn<TContext, TExpressionEvent extends EventObject>(
stateValue: StateValue
): GuardPredicate<TContext, TExpressionEvent, any, any>;
type StateValue = string | { [key: string]: StateValue };Usage Examples:
import { stateIn } from "xstate/guards";
// Check simple state
stateIn("loading")
// Check nested state
stateIn({ editing: "text" })
// Check multiple possible states
stateIn({ form: { validation: "pending" } })
// Use in transitions
{
on: {
SUBMIT: {
guard: stateIn("ready"),
target: "submitting"
}
}
}Low-level function for evaluating guards in different contexts.
/**
* Evaluates a guard against current snapshot and context
* @param guard - Guard to evaluate (function, object, or string reference)
* @param context - Current machine context
* @param event - Current event
* @param snapshot - Current machine snapshot
* @returns Boolean result of guard evaluation
*/
function evaluateGuard<TContext, TExpressionEvent extends EventObject>(
guard: Guard<TContext, TExpressionEvent, any, any>,
context: TContext,
event: TExpressionEvent,
snapshot: AnyMachineSnapshot
): boolean;Usage Examples:
import { evaluateGuard } from "xstate/guards";
// Evaluate guard manually
const isValid = evaluateGuard(
({ context, event }) => context.count > event.threshold,
{ count: 10 },
{ type: "CHECK", threshold: 5 },
currentSnapshot
);
// Evaluate complex guard
const hasAccess = evaluateGuard(
and([
({ context }) => context.user?.active,
({ context }) => context.permissions.includes("read")
]),
context,
event,
snapshot
);Guards defined directly in machine configuration as functions.
// Inline guard function
{
on: {
NEXT: {
guard: ({ context, event }) => context.step < event.maxSteps,
target: "nextStep"
}
}
}Guards defined in machine implementations and referenced by name.
const machine = setup({
guards: {
canProceed: ({ context }) => context.isReady && context.hasData,
isAuthorized: ({ context, event }) => {
return context.user?.permissions?.includes(event.requiredPermission);
}
}
}).createMachine({
on: {
PROCEED: {
guard: "canProceed", // Reference by name
target: "processing"
},
SECURE_ACTION: {
guard: {
type: "isAuthorized",
params: { requiredPermission: "admin" }
},
target: "adminPanel"
}
}
});Guards that accept parameters for reusable conditional logic.
const machine = setup({
guards: {
greaterThan: ({ context }, { value }) => context.count > value,
hasRole: ({ context }, { role }) => context.user?.role === role,
withinTimeframe: ({ context }, { hours }) => {
const now = Date.now();
const diff = now - context.timestamp;
return diff < (hours * 60 * 60 * 1000);
}
}
}).createMachine({
on: {
CHECK_COUNT: {
guard: { type: "greaterThan", params: { value: 10 } },
actions: "processHigh"
},
ADMIN_ACTION: {
guard: { type: "hasRole", params: { role: "admin" } },
actions: "executeAdmin"
},
TIME_SENSITIVE: {
guard: { type: "withinTimeframe", params: { hours: 24 } },
actions: "handleUrgent"
}
}
});Guards with parameters resolved at runtime based on context and event.
{
on: {
VALIDATE: {
guard: {
type: "meetsThreshold",
params: ({ context, event }) => ({
threshold: context.dynamicThreshold,
multiplier: event.urgency || 1
})
},
target: "validated"
}
}
}Complex nested guard logic for sophisticated conditional behavior.
import { and, or, not, stateIn } from "xstate/guards";
const complexGuard = and([
// User must be authenticated
({ context }) => context.user != null,
// Must be in correct state
stateIn({ form: "editing" }),
// Either admin OR (owner AND not suspended)
or([
({ context }) => context.user.role === "admin",
and([
({ context }) => context.user.isOwner,
not(({ context }) => context.user.suspended)
])
]),
// Time-based condition
({ context }) => {
const now = Date.now();
return now - context.lastAction < 300000; // 5 minutes
}
]);Common patterns for form validation and user input checking.
const formGuards = {
// Field validation
hasValidEmail: ({ context }) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(context.form.email);
},
hasValidPassword: ({ context }) => {
const { password } = context.form;
return password && password.length >= 8;
},
passwordsMatch: ({ context }) => {
const { password, confirmPassword } = context.form;
return password === confirmPassword;
},
// Complete form validation
formIsValid: and([
({ context }) => context.form.email?.trim().length > 0,
"hasValidEmail",
"hasValidPassword",
"passwordsMatch"
]),
// Submission readiness
canSubmit: and([
"formIsValid",
not(stateIn("submitting")),
({ context }) => !context.hasErrors
])
};Role and permission checking patterns for access control.
const permissionGuards = {
// Role checks
isAdmin: ({ context }) => context.user?.role === "admin",
isModerator: ({ context }) => context.user?.role === "moderator",
isOwner: ({ context, event }) => context.user?.id === event.resourceOwnerId,
// Permission checks
hasPermission: ({ context }, { permission }) => {
return context.user?.permissions?.includes(permission);
},
// Complex access control
canEdit: or([
"isAdmin",
and([
"isOwner",
({ context }) => !context.resource?.locked
])
]),
canDelete: and([
or(["isAdmin", "isOwner"]),
({ context }) => context.resource?.status !== "published"
])
};Guards maintain full type safety with proper TypeScript integration:
interface Context {
count: number;
user: { name: string; role: "admin" | "user" } | null;
}
type Events =
| { type: "INCREMENT"; amount: number }
| { type: "CHECK_ROLE"; requiredRole: string };
// Type-safe guard with proper context and event types
const typedGuard: GuardPredicate<Context, Events> = ({ context, event }) => {
// context and event are properly typed
if (event.type === "INCREMENT") {
return context.count + event.amount < 100;
}
if (event.type === "CHECK_ROLE") {
return context.user?.role === event.requiredRole;
}
return false;
};Install with Tessl CLI
npx tessl i tessl/npm-xstate