State machines are a very powerful tool but are often underused in web development. The design process forces you to think hard about how you want to model your data, about the different objects lifecycles, about the way you want to expose your data and communicate with your whole team, and about the upcoming evolutions. Going through this process takes a lot of efforts but is worthwile, it brings a lot of structure to your code and your team. Also, the actual implementation of a state machine is usually very simple.
This exists as a living document to define some guidelines regarding state machines and their usage. These guidelines are by no means exhaustive, but hopefully set you on the right path. It is highly recommended that you read the XState docs for more information.
An interesting pattern I stumbled on recently thanks to @davidmarkclementsIterators can be used to encode async state machines if you have a series of conditions you need to check after each async step.