0
# Guards
1
2
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.
3
4
## Capabilities
5
6
### Guard Predicates
7
8
Core guard predicate functions for evaluating conditions based on context and events.
9
10
```typescript { .api }
11
/**
12
* Function type for guard predicates that evaluate conditions
13
* @param args - Guard arguments containing context and event
14
* @param params - Optional parameters passed to the guard
15
* @returns Boolean indicating whether the guard condition is met
16
*/
17
type GuardPredicate<
18
TContext,
19
TExpressionEvent extends EventObject,
20
TParams = undefined,
21
TGuard = any
22
> = {
23
(args: GuardArgs<TContext, TExpressionEvent>, params: TParams): boolean;
24
_out_TGuard?: TGuard;
25
};
26
27
interface GuardArgs<TContext, TExpressionEvent extends EventObject> {
28
/** Current machine context */
29
context: TContext;
30
/** Current event being processed */
31
event: TExpressionEvent;
32
}
33
34
type Guard<TContext, TExpressionEvent extends EventObject, TParams, TGuard> =
35
| GuardPredicate<TContext, TExpressionEvent, TParams, TGuard>
36
| { type: string; params?: TParams }
37
| string;
38
39
type UnknownGuard = Guard<any, any, any, any>;
40
```
41
42
### Logical Combinators
43
44
Logical operators for combining multiple guards into complex conditional logic.
45
46
```typescript { .api }
47
/**
48
* Logical AND guard combinator - evaluates to true if all guards evaluate to true
49
* @param guards - Array of guards to combine with AND logic
50
* @returns GuardPredicate that returns true when all guards are true
51
*/
52
function and<TContext, TExpressionEvent extends EventObject>(
53
guards: Guard<TContext, TExpressionEvent, any, any>[]
54
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;
55
56
/**
57
* Logical OR guard combinator - evaluates to true if any guard evaluates to true
58
* @param guards - Array of guards to combine with OR logic
59
* @returns GuardPredicate that returns true when any guard is true
60
*/
61
function or<TContext, TExpressionEvent extends EventObject>(
62
guards: Guard<TContext, TExpressionEvent, any, any>[]
63
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;
64
65
/**
66
* Logical NOT guard combinator - evaluates to true if the guard evaluates to false
67
* @param guard - Guard to negate
68
* @returns GuardPredicate that returns the opposite of the input guard
69
*/
70
function not<TContext, TExpressionEvent extends EventObject>(
71
guard: Guard<TContext, TExpressionEvent, any, any>
72
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;
73
```
74
75
**Usage Examples:**
76
77
```typescript
78
import { and, or, not } from "xstate/guards";
79
80
// Combine guards with AND logic
81
and([
82
({ context }) => context.isLoggedIn,
83
({ context }) => context.hasPermission,
84
({ event }) => event.type === "SUBMIT"
85
])
86
87
// Combine guards with OR logic
88
or([
89
({ context }) => context.isAdmin,
90
({ context }) => context.isOwner,
91
({ event }) => event.force === true
92
])
93
94
// Negate a guard
95
not(({ context }) => context.isDisabled)
96
97
// Complex combinations
98
and([
99
({ context }) => context.user != null,
100
or([
101
({ context }) => context.user.role === "admin",
102
({ context }) => context.user.role === "moderator"
103
]),
104
not(({ context }) => context.user.suspended)
105
])
106
```
107
108
### State Checking
109
110
Guard for checking if the machine is currently in specific states.
111
112
```typescript { .api }
113
/**
114
* Guard to check if machine is in specific state(s)
115
* @param stateValue - State value to check (string, object, or state ID)
116
* @returns GuardPredicate that returns true when machine matches the state
117
*/
118
function stateIn<TContext, TExpressionEvent extends EventObject>(
119
stateValue: StateValue
120
): GuardPredicate<TContext, TExpressionEvent, any, any>;
121
122
type StateValue = string | { [key: string]: StateValue };
123
```
124
125
**Usage Examples:**
126
127
```typescript
128
import { stateIn } from "xstate/guards";
129
130
// Check simple state
131
stateIn("loading")
132
133
// Check nested state
134
stateIn({ editing: "text" })
135
136
// Check multiple possible states
137
stateIn({ form: { validation: "pending" } })
138
139
// Use in transitions
140
{
141
on: {
142
SUBMIT: {
143
guard: stateIn("ready"),
144
target: "submitting"
145
}
146
}
147
}
148
```
149
150
### Guard Evaluation
151
152
Low-level function for evaluating guards in different contexts.
153
154
```typescript { .api }
155
/**
156
* Evaluates a guard against current snapshot and context
157
* @param guard - Guard to evaluate (function, object, or string reference)
158
* @param context - Current machine context
159
* @param event - Current event
160
* @param snapshot - Current machine snapshot
161
* @returns Boolean result of guard evaluation
162
*/
163
function evaluateGuard<TContext, TExpressionEvent extends EventObject>(
164
guard: Guard<TContext, TExpressionEvent, any, any>,
165
context: TContext,
166
event: TExpressionEvent,
167
snapshot: AnyMachineSnapshot
168
): boolean;
169
```
170
171
**Usage Examples:**
172
173
```typescript
174
import { evaluateGuard } from "xstate/guards";
175
176
// Evaluate guard manually
177
const isValid = evaluateGuard(
178
({ context, event }) => context.count > event.threshold,
179
{ count: 10 },
180
{ type: "CHECK", threshold: 5 },
181
currentSnapshot
182
);
183
184
// Evaluate complex guard
185
const hasAccess = evaluateGuard(
186
and([
187
({ context }) => context.user?.active,
188
({ context }) => context.permissions.includes("read")
189
]),
190
context,
191
event,
192
snapshot
193
);
194
```
195
196
## Guard Configuration
197
198
### Inline Guards
199
200
Guards defined directly in machine configuration as functions.
201
202
```typescript
203
// Inline guard function
204
{
205
on: {
206
NEXT: {
207
guard: ({ context, event }) => context.step < event.maxSteps,
208
target: "nextStep"
209
}
210
}
211
}
212
```
213
214
### Named Guards
215
216
Guards defined in machine implementations and referenced by name.
217
218
```typescript
219
const machine = setup({
220
guards: {
221
canProceed: ({ context }) => context.isReady && context.hasData,
222
isAuthorized: ({ context, event }) => {
223
return context.user?.permissions?.includes(event.requiredPermission);
224
}
225
}
226
}).createMachine({
227
on: {
228
PROCEED: {
229
guard: "canProceed", // Reference by name
230
target: "processing"
231
},
232
SECURE_ACTION: {
233
guard: {
234
type: "isAuthorized",
235
params: { requiredPermission: "admin" }
236
},
237
target: "adminPanel"
238
}
239
}
240
});
241
```
242
243
### Parameterized Guards
244
245
Guards that accept parameters for reusable conditional logic.
246
247
```typescript
248
const machine = setup({
249
guards: {
250
greaterThan: ({ context }, { value }) => context.count > value,
251
hasRole: ({ context }, { role }) => context.user?.role === role,
252
withinTimeframe: ({ context }, { hours }) => {
253
const now = Date.now();
254
const diff = now - context.timestamp;
255
return diff < (hours * 60 * 60 * 1000);
256
}
257
}
258
}).createMachine({
259
on: {
260
CHECK_COUNT: {
261
guard: { type: "greaterThan", params: { value: 10 } },
262
actions: "processHigh"
263
},
264
ADMIN_ACTION: {
265
guard: { type: "hasRole", params: { role: "admin" } },
266
actions: "executeAdmin"
267
},
268
TIME_SENSITIVE: {
269
guard: { type: "withinTimeframe", params: { hours: 24 } },
270
actions: "handleUrgent"
271
}
272
}
273
});
274
```
275
276
### Dynamic Parameters
277
278
Guards with parameters resolved at runtime based on context and event.
279
280
```typescript
281
{
282
on: {
283
VALIDATE: {
284
guard: {
285
type: "meetsThreshold",
286
params: ({ context, event }) => ({
287
threshold: context.dynamicThreshold,
288
multiplier: event.urgency || 1
289
})
290
},
291
target: "validated"
292
}
293
}
294
}
295
```
296
297
## Advanced Guard Patterns
298
299
### Multi-Level Conditions
300
301
Complex nested guard logic for sophisticated conditional behavior.
302
303
```typescript
304
import { and, or, not, stateIn } from "xstate/guards";
305
306
const complexGuard = and([
307
// User must be authenticated
308
({ context }) => context.user != null,
309
310
// Must be in correct state
311
stateIn({ form: "editing" }),
312
313
// Either admin OR (owner AND not suspended)
314
or([
315
({ context }) => context.user.role === "admin",
316
and([
317
({ context }) => context.user.isOwner,
318
not(({ context }) => context.user.suspended)
319
])
320
]),
321
322
// Time-based condition
323
({ context }) => {
324
const now = Date.now();
325
return now - context.lastAction < 300000; // 5 minutes
326
}
327
]);
328
```
329
330
### Form Validation Guards
331
332
Common patterns for form validation and user input checking.
333
334
```typescript
335
const formGuards = {
336
// Field validation
337
hasValidEmail: ({ context }) => {
338
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
339
return emailRegex.test(context.form.email);
340
},
341
342
hasValidPassword: ({ context }) => {
343
const { password } = context.form;
344
return password && password.length >= 8;
345
},
346
347
passwordsMatch: ({ context }) => {
348
const { password, confirmPassword } = context.form;
349
return password === confirmPassword;
350
},
351
352
// Complete form validation
353
formIsValid: and([
354
({ context }) => context.form.email?.trim().length > 0,
355
"hasValidEmail",
356
"hasValidPassword",
357
"passwordsMatch"
358
]),
359
360
// Submission readiness
361
canSubmit: and([
362
"formIsValid",
363
not(stateIn("submitting")),
364
({ context }) => !context.hasErrors
365
])
366
};
367
```
368
369
### Permission-Based Guards
370
371
Role and permission checking patterns for access control.
372
373
```typescript
374
const permissionGuards = {
375
// Role checks
376
isAdmin: ({ context }) => context.user?.role === "admin",
377
isModerator: ({ context }) => context.user?.role === "moderator",
378
isOwner: ({ context, event }) => context.user?.id === event.resourceOwnerId,
379
380
// Permission checks
381
hasPermission: ({ context }, { permission }) => {
382
return context.user?.permissions?.includes(permission);
383
},
384
385
// Complex access control
386
canEdit: or([
387
"isAdmin",
388
and([
389
"isOwner",
390
({ context }) => !context.resource?.locked
391
])
392
]),
393
394
canDelete: and([
395
or(["isAdmin", "isOwner"]),
396
({ context }) => context.resource?.status !== "published"
397
])
398
};
399
```
400
401
## Type Safety
402
403
Guards maintain full type safety with proper TypeScript integration:
404
405
```typescript
406
interface Context {
407
count: number;
408
user: { name: string; role: "admin" | "user" } | null;
409
}
410
411
type Events =
412
| { type: "INCREMENT"; amount: number }
413
| { type: "CHECK_ROLE"; requiredRole: string };
414
415
// Type-safe guard with proper context and event types
416
const typedGuard: GuardPredicate<Context, Events> = ({ context, event }) => {
417
// context and event are properly typed
418
if (event.type === "INCREMENT") {
419
return context.count + event.amount < 100;
420
}
421
if (event.type === "CHECK_ROLE") {
422
return context.user?.role === event.requiredRole;
423
}
424
return false;
425
};
426
```