# Effective State Machines with XState

Canonical: https://davidk.dev/talks/effective-state-machines-with-xstate/
Video: https://www.youtube.com/watch?v=m-dSS55VO3Y
Speaker: David Khourshid (@DavidKPiano), creator of XState
Type: Conference talk · Event: Effect Miami 2 (meetup) · Year: 2026 · Duration: 29:40 · Views: 3021 (as of 2026-10-19)
Topics: XState, Side effects, Effect (TypeScript), State machines

## Summary
Really, I'm going to be talking about two higher level things, effect and XState, and how the combination of them is actually really important. In this example, I have a state machine that's describing a video player.

## Key takeaways (quoted from the transcript)
- Really, I'm going to be talking about two higher level things, effect and XState, and how the combination of them is actually really important. (0:00)
- First of all we start playing the video and if we ever get out of the full state then we pause the video. (9:00)
- The way that I'd like to describe it is that effect is to handling side effects as state machines are to handling behavior. (14:00)
- F first of all this run player which is this whole video player that you could toggle and go full mode mini mode it's all contained in this essentially this big effect. (17:00)

## Chapters
- 0:00 Introduction
- 3:00 We love to just write our async await code and hope…
- 8:00 We should be listening for the video to end…
- 20:00 We have initial transition and so this is from the state…
- 22:00 We can just swap in our layer
- 24:00 Effect is a really well-engineered race car

## Transcript
[0:00] All [applause] right. Thank you. So, this is going to be two things. First of all, it's a little bit of a casual talk. Like, these are my slides. They're not really slides. In fact, it's a giant state machine. I just did it in the tldraw. And also, I am actually pretty new to effect. I've been learning. And how many of you are like effect experts? Exactly. Perfect. So, we're we're in good company here.

[0:00] I'm a beginner. You're a beginner. So, I'm not going to go too deep into effect or be like here's all of the little like intricacies that you need to know. Really, I'm going to be talking about two higher level things, effect and XState, and how the combination of them is actually really important. So, my talk is titled effective state machines with XState. Again, I'm David Khourshid, DavidKP pretty much everywhere online. I also work at my own startup stately.ai where we make it easy for you to make state machines.

[0:00] So I got nerd sniped. So that there was this here I'll actually show you over here Mike Arnaldi like one of the founders of effect. He he wrote an unstable state machine prototype. And of course anything that mentions state machines it's going to nerd snipe me. He actually did manage to summon me over here. So I did materialize my cell in the thread. Essentially, he's trying to create state machines in effect.

[1:00] And I thought that this was really cool. Effect, of course, it's like this giant TypeScript, like the missing standard library of TypeScript. So it makes sense that they want to include as much as they can in there. But this really got me thinking because I think a couple of years ago, two or three years ago, I gave a talk at the first Effect conference over in Austria, and over there I was talking about the combination, but also I acknowledged that there was a little bit of friction because here's the problem.

[2:00] XState wants to own the runtime, but so does Effect. And I thought that was a little backwards. Not Effect's fault. It's actually more of my fault. So, you know, it also got me thinking about like, okay, there's a lot of, you know, people interested in effect. So, you know, I every single time I talk to Claude, eventually the conversation devolves to, hey, you should probably learn Effect and useEffect. So, it got me thinking about Effect, and it got me thinking about XState as well.

[2:00] So the be before I go into the problem, I just wanted to mention that I've been learning from this thing called effect.institute by Kit Langton, really good resource so I highly recommend if you want to get into Effect, definitely go through effect.institute. It's a really easy interactive way of learning Effect and all of the concepts.

[3:00] And I saw this one over here, and this one really spoke to my heart where it was talking about like if you weren't doing Effect you might write code like this. I'm sure that all of you have written just async await code like this. And this really spoke to me because I have been talking a lot about how this code is deceptively simple. But there are so many traps in this code.

[3:00] You know, right now it reads simple. We get the carts, we get the payments, we get the shipments, and we return it. However, there, like I said, there's so many hidden traps. What happens if the cart fails? What happens if the charge fails, the ship fails? So this all assumes that everything is going to work perfectly. But we know, especially once you get into async/await stuff, that's not the case. And so what Effect is really good at is it makes your errors up front and center.

[4:00] It says these errors are part of the type, and if you do not handle these errors, then you know the it won't even compile, so much less run. So you really need to make sure that every single possible error is taken care of. So you know this is what I've been talking about too: how async/await is just too convenient. And in my opinion, this is one of the biggest selling points of Effect, async/await we love to ignore errors.

[4:00] We love to just write our async await code and hope that everything works. But that's not how the real world works. Our apps break in so many different ways. And the example that I gave a few years ago was pretty much exactly like that. It's like what if in this example workflow for making coffee, what if each one of these things fail? So the workflow can fail at any step, but we have nothing that handles it.

[4:00] And effect forces you to handle all of those. Now effect and XState have essentially been approaching the same class of problems at different angles because if I were to represent this in like how something like XState would not solve it but represent it would look like this and I how many of you are familiar with state machines or okay that's an awesome number but for those who aren't this is a state machine and it is as easy as follow the arrow.

[5:00] What it means is that you can only be in one state at a time. So, for example, grind beans, and from each state there are transitions and events cause these transitions. So for example, if we're in grind beans and the ground, so we're we're finished grinding the beans, then we transition to boil water. We can't magically transition from grind beans to make capacito because we're missing his death. we it's everything is represented in the arrows, and you can't just navigate away from that.

[6:00] So just like effect makes it clear like you must handle these things, and these are the descriptions of effect state machines make it clear, both visually and in code they make it clear what you can and can't do and what the overall flow is going to be. And so what I really love about state machines is that with your simple async/await code handling things gets a little bit hairy. You know, you have all of this code over here where it's like, okay, if an error happens, do that.

[6:00] This looks a lot less nice than having await a wait. You're like, okay, we know it's not linear anymore because we have to handle subflows. We have to handle errors. Maybe go back and do a step. So now you're introducing loops. It looks really awkward. But if I wanted to modify this state machine and say, "Hey, if something fails, here's an alternate path." Then it's extremely visual. I could just say an error might happen.

[7:00] So, like let's say the grinder is broken. And we could handle that by going to Publix and buying ground beans. And then once we get those, then we could continue and boil water. This is extremely clear even to people who have not ever done any coding. So that's that's exactly the point. These diagrams are things that you could just share with anyone. You could have them follow the arrows and understand generally what's going on.

[7:00] In fact, you don't need to call them state machines. Just call them flowcharts or whatever they're used to hearing because state machines sound a little bit scary. So what does something like this look like in code? So I'm going to share a little bit of the code. Hopefully that's big enough. Bigger. No. Good. Okay. So in this example, I have a state machine that's describing a video player. This is a classic example I've been giving for years.

[7:00] But the video player here's the behavior.

[8:00] It starts small, but when you click toggle it goes sort of bigger on the web page, and it automatically starts playing. Now, when you press Escape, as you know, might be a normal user behavior it shrinks back to its small form, but it stops playing. Also, when the video is done playing after 2 seconds, it should automatically just shrink again. And also, when you stop playing it or when you shrink it back, it should stop.

[8:00] So all of this is described in the state machine. So, just to go over this XState syntax, I'll hide a few things for a minute. We have two main states. The video player could either be mini, so it's shrunken, or it could be full where it's sort of, you know, bigger. And the way we transition quite simply is a toggle. So, this is an event. So, when a toggle happens, we transition, you can imagine that arrow, to the full states.

[9:00] And so in the full state a few things happen. First of all we start playing the video and if we ever get out of the full state then we pause the video. When we toggle, we go back to mini. So again it will pause the video when we go back to mini. But we also want to handle what happens if we press the escape key. And this is just another custom event.

[9:00] The escape key will do the same thing and go back to the mini state. And then we have some nested states over here. So when we're in full mode, we start in the playing state. And so when the video ends, we go to the stop state. And then after 2 seconds, we go back to the mini state. So this is the entire state machine. And I want to demonstrate what this actually looks like in practice.

[9:00] So I'll just reload. This is our state machine right here. Let me just clean up some of these things. And so you could see if I toggle it will go into full mode. So it transitions from mini to full. And then if I toggle again, it goes back to mini. I could toggle I could press escape and it goes back to mini. And also once the video is over, let's just skip ahead. you're going to see that it actually goes to the stop state over here and then after two seconds it goes back to mini.

[10:00] All of this is described inside this state machine. Now there's a few problems with this. That's me. And it's there are some smelly things. So for example, over here we have action stop play video, but we are actually hard coding what that means over here, and by we I mean Claude code because that's the one who wrote this code, but it's something that I would have written too. We're looking for the video and we're playing it directly and then we're looking for the video and then we're you know pressing pause over here and then you know we just hook it up to React over there.

[11:00] So the two problems with this, first of all, let's say that I wanted this exact same logic to work for YouTube. Now, I can't really hardcode these things in there because this is only for HTML 5 video. It's not for a YouTube player. So, it makes things a little bit more difficult to do over here. Granted, I still don't need to change the entire state machine. I could just change the actions in here, but yeah, it becomes a little bit trickier.

[11:00] The second thing is this over here right now in XState or at least until the next version of XState. This says after 2 seconds transition who owns that clock XState does and you really have no way of customizing that. So I thought this would be a great opportunity to use XState and or sorry to useEffect and to sort of demonstrate you know where effect comes in. So here let's go here. But also I didn't want XState to be the one that's actually running these events.

[12:00] And this is the important point. I think about a year ago or so we introduced an API to XState where essentially it allows you to just call transition on a machine and it becomes a pure function. So basically given a state and an event you get the next state and the effects returned from that and that makes state machines just a description of behavior. So what this means is that XState no longer needs to run the effects.

[13:00] It never did. A state machine is pure. It's just a description, a blueprint of how your application or parts of your application should behave and that's really how it should be and it is actually how it fits really well with effect. So just an example over here we have two functions in XState initial transition which tells you what the initial state is and what the effects are and a transition which tells you given the current state.

[13:00] So like s0 in this case and the events that occurred what's the next state going to be and what effect am I supposed to execute. So XState is not executing the effects, instead it's telling you these are the effects that you should execute. So in this case, I know you can't see that really well because of contrast, but we go to the plain state, and we have all of these effects to execute once we enter that state.

[13:00] So we should be listening for the escape key. We should be listening for the video to end, and we should also execute the play video the play video action so that we could actually start the video. So really this is where Effect comes in and it's right here in the name this tuple that's returned or tupil you know we return the next state and we return the effect to be executed, and we could actually hand that off to Effect, and we get some really nice properties from that.

[14:00] So the way that I'd like to describe it is that effect is to handling side effects as state machines are to handling behavior. Both of them are focused on the description of those not necessarily the execution. So, of course, Effect is used to run these programs. But that doesn't happen in effect until the very end. As you may know, effect is it is your the outputs. It is the types of errors that can occur and it's also the requirements. like those are the three core things that you learn when you start learning Effect and those are basically descriptions of programs.

[15:00] They're the building blocks where you could take them and then you could just finally execute them and provide a layer etc. But that's the main point is that these things essentially are just data until you actually decide to execute them. Now, this gets a little bit complicated, but when actually moving XState into Effect, we're actually using a lot of core effects concepts, which I think are pretty important. So, XState also embodies the actor model.

[15:00] And this is where I know I'll lose some of you, but I'll try to explain it. An actor is just a thing. So, it's an entity that has its own internal state and it responds to messages. So for example, an actor can be what's running the C machine itself. So it could say, hey, I'm in charge of running the video player. So once I've received the message toggle, then internally I'm in the full state and I'm supposed to play the video, etc. and execute different effects and even spawn different actors.

[16:00] And the way that it receives messages is from what's called a mailbox. And so the mailbox receives a bunch of messages and these are execute instead of executed eagerly. And the reason for that is because while this actor is working and processing a message, it can receive other messages. So you want to make sure that there's no collision with that. And to do that, you utilize a queue. And so this is where in Effect, queue works really well.

[16:00] In Effect, Wrath works for internal states. And representing each of the effects as just a function that returns an effect that can be executed works really well for you know for actually running these effects as I'm using the word effect too much but you get what I'm talking about. And also spawning actors which we'll look at in the code that's where effects and fibers really fit in. So this is more of the advanced side of effects, but long story short, effects can take those actions from XState and be the one that executes them.

[17:00] So I want to show you the other version and let's see where is it? Oh yeah, it's over here in interpreter. So this is a few different parts. F first of all this run player which is this whole video player that you could toggle and go full mode mini mode it's all contained in this essentially this big effect. So, we have fibers. We're going to use those later. We have send. And that is our mailbox that we were talking about.

[18:00] Here's the important parts. This run effects. Probably you know what? Let's rename that. That should be run actions just because I'm seeing effects too much. But basically, it takes one of the actions and this is from XState. And then it tries to match what is that action supposed to really do. So for example, when it's play video, then we're getting the video from our video service, which I'll talk about later, and we're doing the play method.

[18:00] And same thing with pause video. We pause the video once we once we have that declarative action. So by the way, in XState, these are what actions look like. They're just objects that tell you here's the name of the action. It might have some optional parameters, but that's it. And then there's there's other things like you know spawning, starting different what we call invoked actions or sorry invoked actors in XState. And so this is for doing things that are ongoing.

[19:00] Like for example, we're listening to a keyboard effect. So we're listening to the escape key. And so if we translate that to what that means. So in this case, we're listening for the event.key to be escape. And then we send the machine back key escape. And also we're listening for the video to end. Now here's the important part. In play video and pause video, it doesn't matter whether it's the HTML 5 video API or the YouTube video API or Vimeo or whatever.

[19:00] And that's exactly the point. We have this service that we're providing so that we could just abstract that away. So that actually fits really nicely. And the other problem that I was talking about where you know with timers XState owning the timer now we just have effect own the timer.

[20:00] So we have effects sleep and then after it sleeps for a while then we could raise that event etc. Okay so continuing on with this is the main pattern. So we have initial transition and so this is from the state machine where we say hey what is the initial state and what are the initial actions and so that's what these two things are. We store the state in a state ref and then we also call a call back that says hey the state just updated and this is what our state is and then for each of the actions so from initial actions we just execute them and so again that's our you know our function of bot that matches on what action we're supposed to execute.

[20:00] And then we do this loop and this loop is going to run essentially forever but in reality until the machine ends. So the way it works is we just take from our mailbox. So again these are a queue of events that it received and then we calculate what is the next state and what are the actions that we're supposed to execute. Those are the two main things that we grab from transition.

[21:00] And so we set the next states to the state ref using an effect ref. And then we update the snapshot and we say, "Hey, the state updated." and then and then we execute each of the effects. So I know that this was a lot to take in. It was a lot for me to take in, especially, you know, being a newbie to effects. But first of all, I like how clear and explicit everything is.

[21:00] Even though you know when you're first coming to effect it could feel pretty unfamiliar but I want you to sort of squint at this and understand the overall pattern where it's essentially this.

[22:00] We get the next state and the next actions from the current state and the events and that's it. You take the actions and you tell effects go execute them and then you store the state somewhere and then you just keep looping through that and saying okay what's my next event what should I do next based on that and to show you what this video service looks like we have these three things over here one of them is just an effects to say leave the video one is to pause the video and one just notifies us when the video ended.

[22:00] So when we actually create different server different layers for this we could easily swap it out whether we're using YouTube. So we have this you video YouTube layer and we could define this is what it means to go full screen to play the video to pause the video and to notify you when the video stops. And we have the HTML 5 API which is somewhere. Oh yeah, right over here. Which honestly much simpler but you know sometimes you want to play YouTube videos.

[23:00] So what does this look like? We go. Okay. One thing to keep in mind is that the machine that I described didn't change at all. It is exactly the same machine. We did not need to modify it for effect. So now when we toggle this, it's working exactly the same way. It's transitioning. We could go we could press escape of course. And we could also see that when it stops, it's going to transition back to mini after a while.

[23:00] And so now we can just swap in our layer. So now we have this YouTube layer. And so we could toggle it.

[24:00] It starts playing. I'm not going to make you sit here for 10 minutes. So, let's just get to the end. Okay, maybe more. 3 seconds. All right. So, then after 2 seconds, it shrinks back down. And so, all we're doing over here is swapping layers. So, that was one of the moments where I'm like, wow, effect. It's like it is so many things but at the end of the day it's a declarative way or semi-declarative way of representing effects.

[24:00] But also it's dependency injection. It's a lot of things that make your programs a lot safer to run than just trying to reinvent all these things yourself. And so that's something that I really appreciate about effect. Wrong one again. So now you know going back to this you know the question is you know should we port to XState to effect or something like that. I think that was something that they were you know experimenting with and I actually mentioned in this thread somewhere that trying to port XState over to effects will do two things.

[25:00] First of all, it will make you extremely good at TypeScript. And secondly, it will make you realize just how difficult it is to force all of the statechart logic into effect just because the algorithms are pretty complex. So, I and I think that's a little bit misguided because instead two things need to change. First of all, you know, effects should just be for effects. Now, that's not to knock that, you know, Effect is trying this state machine thing.

[25:00] I think it's really cool, but at the same time, I think that Effect and XState have different strengths, but they're both complementary. And first of all, I have to stop pretending that XState's runtime was valuable. It's really not. And the true power is that with XState and with state machines in general, you could really just represent your logic as that machine and use it as a pure function to say given the current state in events, this is what the next state is and these are what the effects that you're supposed to execute are going to be.

[26:00] So the machine is the valuable parts and I like to compare the two as a race car and the map. So Effect is a really well-engineered race car. But here's the thing with Effect. It it doesn't matter how good Effect is, you could still write wrong logic inside of Effect. And that's where state machines come in because, sure, Effect makes you handle all of your all of your errors. It makes sure that like everything is completely type- safe.

[26:00] However, it is still possible to just, you know, forgets to make a transition or write some logic that puts the user in an impossible state. Effect does not stop you from that unless you are well aware of that. But with state machines, it makes it so that, you know, when you lay your state machine out, it is mathematically impossible to go to states or you know things like that. So you could make a race car, this is the analogy, you can make a race car as well engineered as possible, but it could still get lost in the city.

[27:00] So that's what the map is for. And that's where I think, you know, XState is still extremely valuable. So the combination of them is something that I'm excited about. And I'm also working on XState V6 to make it just a lot more type-safe and compatible with things like Effect. So, before I end here I wanted to show you that there are people who are doing you know just this combination of state machines and Effect even in practice today.

[28:00] So, Joel actually recently launched this workshop, and it's on YouTube, and also there's a GitHub link over here where, if you're working with agents, he's doing what's called loopcraft and pi, which is a coding harness, and he's combining XState and Effect and a few other things in really creative ways to control agent behavior and to make agents do some really cool things. Another one, Foldkit. If you haven't heard of Foldkit, it's sort of like React meets Effect.

[28:00] So, it is that UI library for Effect, which is really cool. And they are adding state machines. So, this is an exciting project that if you're in the Effect community and you know, just want to want to see what other people are doing, Foldkit is definitely something to follow. And so they are experimenting with using state machines inside of there. And also one of my friends, Sandro Maglione, he has also been experimenting with state machines and effect, but also he's really knowledgeable at both Effect and XState too.

[29:00] So he's definitely worth following seeing what he puts out there. So yeah, overall I'll stop there for questions and thank you.
