UNPKG

16.5 kBMarkdownView Raw
1<p align="center">
2 <br />
3
4 <picture>
5 <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/statelyai/public-assets/main/logos/xstate-logo-white-nobg.svg">
6 <img alt="XState logotype" src="https://raw.githubusercontent.com/statelyai/public-assets/main/logos/xstate-logo-black-nobg.svg" width="200">
7 </picture>
8 <br />
9 <strong>Actor-based state management & orchestration for complex app logic.</strong> <a href="https://stately.ai/docs">→ Documentation</a>
10 <br />
11 <br />
12</p>
13
14XState 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.
15
16It 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.
17
18### ✨ Create state machines visually in Stately Studio → [state.new](https://state.new)
19
20---
21
22📖 [Read the documentation](https://stately.ai/docs)
23
24➡️ [Create state machines with the Stately Editor](https://stately.ai/editor)
25
26🖥 [Download our VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)
27
28📑 Inspired by the [SCXML specification](https://www.w3.org/TR/scxml/)
29
30💬 Chat on the [Stately Discord Community](https://discord.gg/xstate)
31
32## Templates
33
34Get started by forking one of these templates on CodeSandbox:
35
36<table>
37<thead>
38<tr><th>Template</th><th></th></tr>
39</thead>
40<tbody>
41
42<tr>
43<td>
44
45[🤖 XState Template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/vanilla-ts)
46
47[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/vanilla-ts?file=%2Fsrc%2FfeedbackMachine.ts)
48
49</td>
50<td>
51
52- XState v5
53- TypeScript
54- _No framework_
55
56</td>
57</tr>
58
59<tr>
60<td>
61
62[⚛️ XState + React Template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/react-ts)
63
64[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/react-ts?file=%2Fsrc%2FfeedbackMachine.ts)
65
66</td>
67<td>
68
69- [React](https://react.dev/)
70- XState v5
71- TypeScript
72
73</td>
74</tr>
75
76<tr>
77<td>
78
79[💚 XState + Vue Template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/vue-ts)
80
81[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/vue-ts?file=%2Fsrc%2FfeedbackMachine.ts)
82
83</td>
84<td>
85
86- [Vue](https://vuejs.org/)
87- XState v5
88- TypeScript
89
90</td>
91</tr>
92
93<tr>
94<td>
95
96[🧡 XState + Svelte Template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/svelte-ts)
97
98[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/svelte-ts?file=%2Fsrc%2FfeedbackMachine.ts)
99
100</td>
101<td>
102
103- [Svelte](https://svelte.dev/)
104- XState v5
105- TypeScript
106
107</td>
108</tr>
109
110</tbody>
111</table>
112
113## Super quick start
114
115```bash
116npm install xstate
117```
118
119```ts
120import { createMachine, createActor, assign } from 'xstate';
121
122// State machine
123const toggleMachine = createMachine({
124 id: 'toggle',
125 initial: 'inactive',
126 context: {
127 count: 0
128 },
129 states: {
130 inactive: {
131 on: {
132 TOGGLE: { target: 'active' }
133 }
134 },
135 active: {
136 entry: assign({ count: ({ context }) => context.count + 1 }),
137 on: {
138 TOGGLE: { target: 'inactive' }
139 }
140 }
141 }
142});
143
144// Actor (instance of the machine logic, like a store)
145const toggleActor = createActor(toggleMachine);
146toggleActor.subscribe((state) => console.log(state.value, state.context));
147toggleActor.start();
148// => logs 'inactive', { count: 0 }
149
150toggleActor.send({ type: 'TOGGLE' });
151// => logs 'active', { count: 1 }
152
153toggleActor.send({ type: 'TOGGLE' });
154// => logs 'inactive', { count: 1 }
155```
156
157## [Stately Studio](https://stately.ai)
158
159- Visually create, edit, and collaborate on state machines
160- Export to many formats, including XState v5
161- Test path & documentation autogeneration
162- Deploy to Stately Sky
163- Generate & modify machines with Stately AI
164
165<a href="stately.ai/registry/new?ref=github" title="Stately Studio">
166 <img src="https://github.com/statelyai/xstate/assets/1093738/74ed9cbc-b824-4ed7-a16d-f104947af8a7" alt="XState Viz" width="800" />
167</a>
168
169**[state.new](https://stately.ai/registry/new?ref=github)**
170
171## Why?
172
173Statecharts 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.
174
175Read [📽 the slides](http://slides.com/davidkhourshid/finite-state-machines) ([🎥 video](https://www.youtube.com/watch?v=VU1NKX6Qkxc)) or check out these resources for learning about the importance of finite state machines and statecharts in user interfaces:
176
177- [Statecharts - A Visual Formalism for Complex Systems](https://www.sciencedirect.com/science/article/pii/0167642387900359/pdf) by David Harel
178- [The World of Statecharts](https://statecharts.github.io/) by Erik Mogensen
179
180## Packages
181
182| Package | Description |
183| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
184| 🤖 `xstate` | Core finite state machine and statecharts library + interpreter |
185| [📉 `@xstate/graph`](https://github.com/statelyai/xstate/tree/main/packages/xstate-graph) | Graph traversal utilities for XState |
186| [⚛️ `@xstate/react`](https://github.com/statelyai/xstate/tree/main/packages/xstate-react) | React hooks and utilities for using XState in React applications |
187| [💚 `@xstate/vue`](https://github.com/statelyai/xstate/tree/main/packages/xstate-vue) | Vue composition functions and utilities for using XState in Vue applications |
188| [🎷 `@xstate/svelte`](https://github.com/statelyai/xstate/tree/main/packages/xstate-svelte) | Svelte utilities for using XState in Svelte applications |
189| [🥏 `@xstate/solid`](https://github.com/statelyai/xstate/tree/main/packages/xstate-solid) | Solid hooks and utilities for using XState in Solid applications |
190| [✅ `@xstate/test`](https://github.com/statelyai/xstate/tree/main/packages/xstate-test) | Model-Based-Testing utilities (using XState) for testing any software |
191| [🔍 `@xstate/inspect`](https://github.com/statelyai/xstate/tree/main/packages/xstate-inspect) | Inspection utilities for XState |
192
193## Finite State Machines
194
195<table>
196<thead><tr><th>Code</th><th>Statechart</th></tr></thead>
197<tbody>
198<tr>
199<td>
200
201```js
202import { createMachine, createActor } from 'xstate';
203
204const lightMachine = createMachine({
205 id: 'light',
206 initial: 'green',
207 states: {
208 green: {
209 on: {
210 TIMER: 'yellow'
211 }
212 },
213 yellow: {
214 on: {
215 TIMER: 'red'
216 }
217 },
218 red: {
219 on: {
220 TIMER: 'green'
221 }
222 }
223 }
224});
225
226const actor = createActor(lightMachine);
227
228actor.subscribe((state) => {
229 console.log(state.value);
230});
231
232actor.start();
233// logs 'green'
234
235actor.send({ type: 'TIMER' });
236// logs 'yellow'
237```
238
239<table>
240<thead><tr><th>Code</th><th>Statechart</th></tr></thead>
241<tbody>
242<tr>
243<td>
244
245</td>
246<td>
247
248</td>
249</tr>
250</tbody>
251</table>
252
253</td>
254<td>
255
256<a href="https://stately.ai/registry/editor/fa443471-b416-4014-8e6f-12417863e4d4?mode=design&machineId=27e86036-f2f7-40f1-9d1e-66ce6e1accc0" title="Finite states">
257 <img src="https://github.com/statelyai/xstate/assets/1093738/36d4b6b5-e3d0-4c19-9f41-2e3425ceac88" alt="Finite states" width="400" />
258 <br />
259 Open in Stately Studio
260</a>
261<br />
262
263</td>
264</tbody>
265</table>
266
267## Hierarchical (Nested) State Machines
268
269<table>
270<thead><tr><th>Code</th><th>Statechart</th></tr></thead>
271<tbody>
272<tr>
273<td>
274
275```js
276import { createMachine, createActor } from 'xstate';
277
278const pedestrianStates = {
279 initial: 'walk',
280 states: {
281 walk: {
282 on: {
283 PED_TIMER: 'wait'
284 }
285 },
286 wait: {
287 on: {
288 PED_TIMER: 'stop'
289 }
290 },
291 stop: {}
292 }
293};
294
295const lightMachine = createMachine({
296 id: 'light',
297 initial: 'green',
298 states: {
299 green: {
300 on: {
301 TIMER: 'yellow'
302 }
303 },
304 yellow: {
305 on: {
306 TIMER: 'red'
307 }
308 },
309 red: {
310 on: {
311 TIMER: 'green'
312 },
313 ...pedestrianStates
314 }
315 }
316});
317
318const actor = createActor(lightMachine);
319
320actor.subscribe((state) => {
321 console.log(state.value);
322});
323
324actor.start();
325// logs 'green'
326
327actor.send({ type: 'TIMER' });
328// logs 'yellow'
329
330actor.send({ type: 'TIMER' });
331// logs { red: 'walk' }
332
333actor.send({ type: 'PED_TIMER' });
334// logs { red: 'wait' }
335```
336
337</td>
338<td>
339<a href="https://stately.ai/registry/editor/fa443471-b416-4014-8e6f-12417863e4d4?mode=design&machineId=30dffcdd-16c2-49e2-bfc6-a674057cb271" title="Hierarchical states">
340 <img src="https://github.com/statelyai/xstate/assets/1093738/32b0692b-1c29-4469-b5e3-03146e3ef249" alt="Hierarchical states" width="400" />
341 <br />
342 Open in Stately Studio
343</a>
344<br />
345</td>
346</tr>
347</tbody>
348</table>
349
350## Parallel State Machines
351
352<table>
353<thead><tr><th>Code</th><th>Statechart</th></tr></thead>
354<tbody>
355<tr>
356<td>
357
358```ts
359import { createMachine, createActor } from 'xstate';
360
361const wordMachine = createMachine({
362 id: 'word',
363 type: 'parallel',
364 states: {
365 bold: {
366 initial: 'off',
367 states: {
368 on: {
369 on: { TOGGLE_BOLD: 'off' }
370 },
371 off: {
372 on: { TOGGLE_BOLD: 'on' }
373 }
374 }
375 },
376 underline: {
377 initial: 'off',
378 states: {
379 on: {
380 on: { TOGGLE_UNDERLINE: 'off' }
381 },
382 off: {
383 on: { TOGGLE_UNDERLINE: 'on' }
384 }
385 }
386 },
387 italics: {
388 initial: 'off',
389 states: {
390 on: {
391 on: { TOGGLE_ITALICS: 'off' }
392 },
393 off: {
394 on: { TOGGLE_ITALICS: 'on' }
395 }
396 }
397 },
398 list: {
399 initial: 'none',
400 states: {
401 none: {
402 on: {
403 BULLETS: 'bullets',
404 NUMBERS: 'numbers'
405 }
406 },
407 bullets: {
408 on: {
409 NONE: 'none',
410 NUMBERS: 'numbers'
411 }
412 },
413 numbers: {
414 on: {
415 BULLETS: 'bullets',
416 NONE: 'none'
417 }
418 }
419 }
420 }
421 }
422});
423
424const actor = createActor(wordMachine);
425
426actor.subscribe((state) => {
427 console.log(state.value);
428});
429
430actor.start();
431// logs {
432// bold: 'off',
433// italics: 'off',
434// underline: 'off',
435// list: 'none'
436// }
437
438actor.send({ type: 'TOGGLE_BOLD' });
439// logs {
440// bold: 'on',
441// italics: 'off',
442// underline: 'off',
443// list: 'none'
444// }
445
446actor.send({ type: 'TOGGLE_ITALICS' });
447// logs {
448// bold: 'on',
449// italics: 'on',
450// underline: 'off',
451// list: 'none'
452// }
453```
454
455</td>
456<td>
457<a href="https://stately.ai/registry/editor/fa443471-b416-4014-8e6f-12417863e4d4?mode=design&machineId=980f50d8-e1ff-4441-8c8b-afe41c1610f2" title="Parallel states">
458 <img src="https://github.com/statelyai/xstate/assets/1093738/3b1989c0-f4a9-4653-baf2-4df3a40e91a6" alt="Parallel states" width="400" />
459 <br />
460 Open in Stately Studio
461</a>
462</td>
463</tr>
464</tbody>
465</table>
466
467## History States
468
469<table>
470<thead><tr><th>Code</th><th>Statechart</th></tr></thead>
471<tbody>
472<tr>
473<td>
474
475```js
476import { createMachine, createActor } from 'xstate';
477
478const paymentMachine = createMachine({
479 id: 'payment',
480 initial: 'method',
481 states: {
482 method: {
483 initial: 'cash',
484 states: {
485 cash: {
486 on: {
487 SWITCH_CHECK: 'check'
488 }
489 },
490 check: {
491 on: {
492 SWITCH_CASH: 'cash'
493 }
494 },
495 hist: { type: 'history' }
496 },
497 on: { NEXT: 'review' }
498 },
499 review: {
500 on: { PREVIOUS: 'method.hist' }
501 }
502 }
503});
504
505const actor = createActor(paymentMachine);
506
507actor.subscribe((state) => {
508 console.log(state.value);
509});
510
511actor.start();
512// logs {
513// value: { method: 'cash' },
514// }
515
516actor.send({ type: 'SWITCH_CHECK' });
517// logs {
518// value: { method: 'check' },
519// }
520
521actor.send({ type: 'NEXT' });
522// logs {
523// value: 'review',
524// }
525
526actor.send({ type: 'PREVIOUS' });
527// logs {
528// value: { method: 'check' },
529// }
530```
531
532</td>
533<td>
534<a href="https://stately.ai/registry/editor/fa443471-b416-4014-8e6f-12417863e4d4?mode=design&machineId=d1a9bb95-db97-4af3-b38b-71b005c643d3" title="History states">
535 <img src="https://github.com/statelyai/xstate/assets/1093738/1be5c495-d560-4660-94f2-5341efbf7128" alt="History state" width="400" />
536 <br />
537 Open in Stately Studio
538</a>
539</td>
540</tr>
541</tbody>
542</table>
543
544## Sponsors
545
546Special thanks to the sponsors who support this open-source project:
547
548<img src="https://opencollective.com/xstate/tiers/backer/badge.svg?label=sponsors&color=brightgreen" />
549
550<a href="https://transloadit.com/?utm_source=xstate&utm_medium=referral&utm_campaign=sponsorship&utm_content=github">
551 <picture>
552 <source media="(prefers-color-scheme: dark)" srcset="https://assets.transloadit.com/assets/images/sponsorships/logo-dark.svg">
553 <source media="(prefers-color-scheme: light)" srcset="https://assets.transloadit.com/assets/images/sponsorships/logo-default.svg">
554 <img src="https://assets.transloadit.com/assets/images/sponsorships/logo-default.svg" alt="Transloadit Logo">
555 </picture>
556</a>
557
558## SemVer Policy
559
560We 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.
561
562### Breaking changes
563
564XState 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.
565
566Any 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.
567
568### TypeScript changes
569
570We 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.
571
572For 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.
573
574### Packages
575
576Most 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.