# Migrating to XState v5 beta webinar

Canonical: https://davidk.dev/talks/migrating-to-xstate-v5-beta-webinar/
Video: https://www.youtube.com/watch?v=s-nqoIbIZac
Speaker: David Khourshid (@DavidKPiano), creator of XState
Type: Webinar · Event: Stately webinar · Year: 2023 · Duration: 1:04:37 · Views: 1924 (as of 2026-10-19)
Topics: XState, Actor model, TypeScript

## Summary
XState v5 beta is here! Join David and Laura to learn the steps needed to migrate to XState v5 beta, and we’ll answer any questions you have about XState.

## Key takeaways (quoted from the transcript)
- Hi friends, David and I are here today to give you a guide to migrating from XState version 4 to XState version 5. (0:00)
- As you would expect in previous versions of XState v4, the assign action was always prioritized, and so now things just work predictably by default. (9:00)
- We could use the from promise function and that's imported directly from XState, and so in from promise. (16:00)
- We would have to create a function that creates a machine, so in other words, a machine factory, so, let's call this create feedback machine. (19:00)
- The always events, now need to be defined through the always property, and you can't define them via an empty string as well. (38:00)
- You could say on some event, instead of in two, for instance, you would say, guard, state in. (59:00)

## Chapters
- 0:00 Introduction
- 8:00 Oops, XState@Beta and also we're going to add @XState/React@Beta
- 11:00 I like to shorten my context in V4 as CTX…
- 13:00 I'm just going to add something for submitting so that we…
- 15:00 You have create feedback machine…
- 23:00 Long story short is in the future we're going to be…
- 27:00 In normal projects, you should use Typegen, but yeah…
- 30:00 By the way, I'm just going to change this to just…
- 43:00 You could access that actor again from any level…
- 46:00 Docs when, as we mentioned earlier…
- 48:00 Instead, what we have to do is provide input over here…
- 55:00 You could say on some event, instead of in two…
- 1:02:00 Be sure to join us because office hours is always great…

## Transcript
[0:00] Hi friends, David and I are here today to give you a guide to migrating from XState version 4 to XState version 5. XState V5 is in beta now and we're really excited to have you try it out. If you have any questions for us during this webinar, you can ask us in the comments on YouTube. Or if you want to post on any of the other social media platforms. And ask us on there.

[0:00] I'll keep an eye out for you or on Discord. So please feel free to ask us any questions at any time and we will pick those up from you as so, without further ado, david, do you want to give us a little brief intro about what XState is for folks who may not be familiar yet? Yeah, sure, so XState is a state machine and statechart library, in fact, it does a little bit more.

[0:00] It's you could think of it as a state orchestration library, so you have state management, and then state orchestration takes that a little bit further. It allows you to manage disparate stores of states and, communicate between them. So, uh? XState version 4 was released about seven or eight years ago, and it's been, or, sorry, not version four XState itself, but yeah, so version four has been out for a while, and we recently just released XState version 5 beta.

[1:00] So with this beta release we have a bunch of, the core features in, actually, we have most of the core features in, and those are considered to be the stable API. And so no breaking changes are, at least no intentional breaking changes are anticipated at this time with XState version 5. And so right now we're just releasing it to let people play around with it and get a feel for the new API.

[1:00] Fabulous. And just in case you don't, you haven't watched any of our videos before and you have no idea who we are. I forgot to do that, but so uh. We are part of the Stately team, and uh. State Studio is a way in which you can visualize the state machines that you might have been building. With XState and I do advocacy and education for Stately and David, you are founder, creator of XState.

[2:00] Where in case you couldn't guess. That already, yeah. So, without further ado, you got a demo that you want to share with us? To help us understand how we're going to migrate from V4 to V5? Yeah, that's right, so let's load it up over here. And by the way, this is not going to be an intro to XState. Webinar. If you do want to see that one, we do have a previous webinar.

[2:00] With actually this exact same example, where this is a feedback form and you could do a few things in here. You could click good in response to how was your experience? And it will tell you. Thanks for your feedback. We could close it, provide more feedback if you click bad. It opens up a form and you can see the submit button is disabled. And you could give any sort of feedback. And then you could submit that and that will show on the thanks page, also on any page, you could just close the form.

[3:00] So this is represented by a state machine. I'll open the visual editor over here, and, this state machine shows the flow of logic in the application. So if I just zoom in a bit, we could see that we start with the prompts and let me zoom in a bit. And so, depending on what you click, for example, if you click the bat button, then it takes you to the form where you could update the form.

[3:00] And if it is valid, then it takes you to the thanks state. And of course, at any state, you could go to closed and then it will just transition you back to closed, so that is the state machine. And by the way, this is the VS code extension. It does not currently work fully yet with XState version 5 beta. It does accidentally work with some things, but there are things that we still need to add compatibility to.

[4:00] So what we're going to be doing is we're going to be taking that example, and, let me just go ahead and close this. Somehow, there we go. And we are going to be converting this example from the previous webinar to XState V5. So right now it's written in XState V4. And just to give you a tour of things first, we have the. Schema and this schema represents our types, so we have the context in the events, and, in XState V4, you would provide that in schema, we also have context and the feedback.

[4:00] Here. The initial value is just an empty string. Then we have a few transitions, so in the prompt state, which is our initial state. We're transitioning to thanks on feedback.good or form on feedback.bad in the form states. We have a few transitions, too, where feedback.update does not do a transition, but rather it just updates the feedback value of the context. So we're just grabbing that from the event and assigning the event.value to that feedback.

[5:00] And then, in order to submit, the feedback has to have a length of greater than zero. This is known as a guarded transition. And so when this condition evaluates to true and this submit event is given, or sends over to the machine, then we go to the thanks states. And we're going to be adding a few features along the way just to demonstrate some more features of XState V5, such as invocations and things like that.

[5:00] Also, at any state, we could go to the closed state. All right, so let's get started. Oh, and by the way, feel free to ask any questions in the comments, or on Twitter or wherever else. Laura will be fielding them. Yeah, if you're getting confused, just let us know. If you want us to slow down or repeat something, let us know. If you have any questions at all, just give us a shout.

[6:00] Yep. And also, I quickly want to mention that we do have, in progress documentation at Stately..ai docs / XState-V5. These docs are in progress. But we feel it's very important for you to get talks earlier rather than later. So that you don't have to ask a lot of questions in the Discord or find us on Twitter. We just want to make the information available to you as soon as possible. In there, uh.

[6:00] We'll be looking at this later, but there's also a migration guide, so we'll be taking a look at that later. Okay, so let's get started. Let's assume that you have this state machine written in XState V4 and you want to upgrade to XState V5. So the first thing I'm going to. Do. And actually, I'm going to bump up my font size just because I feel like we could do a little bit better over here.

[7:00] Can always be bigger. Yeah, and the screen just makes it pretty small, so, font size, and it has changed my terminal size as well. Hopefully that's, yeah, I think that should be good. Yeah, I got it on a smaller screen and it's readable to me right now, so hopefully that's all good again. If you in the comments, if you're struggling with the font size, let us know, we can always bump it up a bit more.

[7:00] Yeah, so if you're wondering where this code lives, by the way, this is in the XState repo under the templates directory. And so this is the React TS project. However, the majority of what we're going to be doing is going to be in XState itself, which is framework independent. You could use it pretty much anywhere that JavaScript runs. So if you're working on a Vue project, Angular, Svelte, solid, any of those frameworks, or even no framework at all, this same information is going to apply.

[8:00] All right?

[8:00] So first things first, let us add or install if you're using npm yarn, add. Oops, XState@Beta and also we're going to add @XState/React@Beta. So all the packages need to be at beta in order for them to be compatible with each other. All right, so we added those, and we're going to see that now that we added it. We have a few red squigglies, which is good. Because this is, the old V4 syntax, and it's highlighting where, where things are going to be a little different.

[8:00] So, let's actually start at the bottom. First of all, you no longer need this. Predictable action arguments true, because this is the, I want to say, the default behavior, but this is the behavior in version five. Just a reminder, this predictable action arguments. Flag meant that when you have multiple actions such as assign and something else, it ensures that those actions run in order. As you would expect in previous versions of XState v4, the assign action was always prioritized, and so now things just work predictably by default.

[9:00] Another thing over here, this will actually error because closed this is on the root state. And, of course, there is no sibling states to, the root states of create machine. So you have to add a dot in front of it. Because we are technically, or, yeah, we are definitely targeting a child's state. So that's just a long-standing bug that, you know, we've fixed all right. So at this point, you know, nothing works, because, you know, we still have a few errors.

[9:00] So another thing, too. In XState V4, we had this multi-argument way of doing things like actions and guards and services and things like that, where you would have context, events and other stuff. However, in XState version 5 there's only one argument, and it's this object where you could destructure whatever you want. So I'm going to grab the event from here. And so now you could see that, we still have writing. We're going to fix that in a minute, but we no longer have to add an underscore for that argument that we that's unused.

[10:00] Okay, so let's actually real quick go back up here and make the types work. This has been renamed to types. So now that we do this, we're going to see that TypeScript is now happy with us. It infers the events as a feedback..update event, and it gives us the value as a string. So all good over there.

[11:00] Another big change is that instead of Cond. However, you pronounce that it's now a guard, because this is a guarded transition, and a guarded transition has a guard. So it makes it a little bit less confusing. I like to shorten my context in V4 as CTX, it's a little bit easier to type, however, thankfully with V5 idle complete, we'll just give you context. So I could just do that. If you do prefer to not, you know, find, replace, and, do all that, you could always just easily do this if you wanted to.

[11:00] I like the more verbose version, it's just it's just more explicit about what it is. But the context we see that feedback is a string, so this is all going to be properly typed. So again, we're replacing Cond with guard, all right, I'll pause there for questions as we're refactoring, uh. One question from Nick is, will there be an update script to help us? Migrate, yeah, yeah, so first of all, we've, we've pretty much already migrated.

[12:00] It's been like five minutes. But, and this is a basic machine, but you could see that it doesn't take that long. Uh? However, we have Mark Chandler, who's really helping us. With a code mod, and the code mod is going to make all of the update tasks a lot easier. So, yeah, long story short, yes, there will be fabulous. And that is all our questions for now. Oh wow, yeah, feel free to ask more questions everyone.

[12:00] Okay, so this is create machine. Oops. I did not mean to go there. Let's go back. Ah, this isn't updating, that's why let me just find the right URL. It's somewhere here. There we go, that's one we want. All right. So when we reload, okay, we see that everything is still working. Yeah, okay, good, everything's working great.

[13:00] So, trying to think those are pretty much like the most basic, migrations that you could do, just a lot of things to keep in mind, uh. But there's another big one and we're going to add a state to represent this. There's another big one that has to do with invocations, so invocations are a little bit different in XState, V5. So I'm going to go ahead and create a submitting state, it's like that you could also do it in the visual editor if you want to, but it's simple enough to add here.

[13:00] And, also at the same time, we are going to, I'm gonna go to the app.tsx file. And I'm just going to add something for submitting so that we could see that it's submitting. So we have state.matches form, all right, so let's add one more state.matches submitting, and we're going to add this a very basic submitting. Okay, cool, so now I'm going to go back to the machine. Oops and problems not used to it yet.

[14:00] Okay, so now we have submitting and let's just do a fake timer. This is still V4, but it works in V5 as well. So we could say, after, let's say, two seconds, we go to thanks. Um. And so now this is going to target the submitting state, this is just to see that it works and. This also demonstrates how XState could be really useful for just prototyping logic. So that wasn't, you know, wasn't too bad to add.

[14:00] So now when we click bad and we get some feedback, it's going to say submitting in a very ugly UI, but it still works.

[15:00] And then it says, thanks for your feedback, okay, so now let's actually make that hit some sort of API. And so to do this, first of all, I'm going to make a fake promise. Because, a lot of front-end developers, and, you know, back-end, have their logic in async functions. So let's just make a like, an async, async function. Submit feedback and we'll, we'll keep this empty for now. Thanks, Copilot. We're going to return the new promise that resolves after two seconds, and it doesn't really take any feedback.

[15:00] Okay, so in version four, the way that we would invoke a promise is we would invoke source. And then we would have to give it context. Events. And then we would have to, you know, provide that promise over there. But now the interface is actually a lot more flexible, and this is one of the changes I'm most excited about. Let's say we want to, invoke a promise. We could use the from promise function and that's imported directly from XState, and so in from promise.

[16:00] We pass in a function that returns to promise. In this case, submit feedback is a function that returns the promise. So I could just put submit feedback right over here. And then we could say on, done, thanks, all right. So now if we reload this, we might get an error, so let me just see why that error is happening. And, yeah, no idea. Oh, you know what it's? I'm just kidding. I thought that I forgot to run it or something that's weird, so it just crashed on me anyway.

[16:00] It is working now, so we have some feedback, make errors more clear, and then that's going to submit. And after two seconds it takes you to thanks for your feedback. And so it is hitting that promise. And you know, everything is good, so that is, that's the big difference. With invoke, where now you have these, these, what do we want to call? Them like logic creators. I'll pause there for questions or anything.

[17:00] Any questions just let us know in the comments. Or you can at us on social media. I'm just, pinging my way around the different social media platforms. See if we have any questions on there. Nothing so far. Awesome, yeah, I, I didn't want to make this. This migration too short because, otherwise it would seem too easy. We can't have that, uh. So, there's another big area of XState V5, and this is more of a new feature rather than the migration, but you could think of it as a migration as well.

[18:00] So I'm going to show you the old way, first that we would do this, and then the new way. So let's say that we wanted to give initial feedback, like just pre-fill a template or something like that. For example, we want to say, I did not have a good experience because, and then we wanted to just pre-fill that. So that when the user goes to the bad page, if it, if it reloads for me, which is a demo.

[18:00] Gods, they go something against you. Yeah, oh, oh that. Hmm. That's why it's because it's not a controlled component. Probably, we could fix that, so we have text area. Yeah, okay, so value equals, and then we are going to get that from the state over here, so oops, value equal, state dot, context, dot, feedback. Okay, cool. So now when we go here, it shows this. I did not, I know, you probably can't read that, but it says I did not have a good experience.

[19:00] Because. And then the user could just, fill that out, so because, fill this out, and then we could submit that feedback. After two seconds, it'll show. But let's say that you actually wanted to customize that initial data, so the initial value object. The way that we did it before is. We would have to create a function that creates a machine, so in other words, a machine factory, so, let's call this create feedback machine.

[19:00] And we might have something like initial feedback, which is a string and we pass it into here, and then we would just pass in that initial feedback. So we would have to change a. Few things, of course, this is not going to work over here because, first of all, this doesn't exist. You have create feedback machine, and we would need to pass in create feedback machine as a function and then provide it with some initial feedback.

[20:00] Just a quick review in XState, XState React in the useMachine hook. If you don't want to memoize this, then there's a way to auto-memoize it. And basically meaning only create it once, and that's bypassing in a function that returns the machine. This is a little bit of an awkward pattern because as developers, we could see that, okay, this is always going to return the same machine. But because functions are black boxes, it's just, you know, still uncertain what's going to happen over here.

[20:00] So let's click bad. We have some initial feedback, which is great, but again, this pattern is a little bit awkward, so let's do better. I'm going to change this back to feedback machine, so we still have our static machine. We're going to get rid of this and now instead, we are going to have dynamic context based on input, so input is a new concept to XState. I'm just gonna put input over here and we're going to wrap this in parentheses.

[21:00] And input is actually, in my opinion, a much safer and more robust way of providing initial data to context. So let's just say that this is input, dot, initial feedback, or an empty string. Thanks, Copilot. And so now we don't have to have that create machine factory function thingy again. Because, you know, we could just use input, so how does this input propagate from where we actually use it? So instead of doing this, so useMachine.

[21:00] We could just pass in the normal feedback machine and then provide the input over here so we could say input, initial feedback. Just kidding, this is great, and, let's make sure that this works. There we go, it says, just kidding, this is great. So yeah, if you are not using, XState React. These these arguments that are, you know, or sorry. These options that are passed to the second argument of useMachine are the same as you would pass to the second arguments of the interpret function.

[22:00] So if I have const. Actor equals interpret feedback machine. You pass an input to that function too, so input something, and then, of course, you could start it all right. I'll pause there for questions. Yeah, going back to the with the promise, with invoking your promises.

[23:00] Will these from promises be typed, or do we need to specify them in the types? Still, yeah, so, they're not automatically typed, at least not yet. In fact, Mateusz has a or Mateusz is working on a PR or coordinating with the TypeScript team. To, to have inferred generic, generic types, just automatically inferred. I'm sorry, I. They phrased that really poorly, but, basically. Long story short is in the future we're going to be able to infer types like from promise, etc.

[23:00] From the actual machine definition. But, pretty soon. I think we still have to add support for this. But it would just be in here, so you would have actors, and then you would have a bunch of different actors. In there. So I actually, since this is fairly new, I actually have to remember the syntax for that. So, here's, um. But it would be in here and so you could just specify, like different types of actors, that you would want.

[24:00] So, yeah, just see if I could find that real quick, or at least find an example. Yeah, I'll have to get back to you on that. But we're still working on the typing story. And, all you have to know is that everything is going to be here inside of the types. I'm going to make a note of it and maybe we can show you in office hours. Yeah, so thank you for the reminder.

[24:00] But yeah, you will be able to strongly type them. Long story short. Now did. Where did? John asks. How would you handle a timeout on the invokes service? All right, so let's say that this promise is taking three seconds. And we're like, Oh, you know what we want to? You know, just fail if it, if it, times out or something like that. So we'll go directly to the closed state if it fails.

[25:00] Just because I'm too lazy to make an error page, but we could just. Say, after, let's say, you know, one and a half seconds, we go to closed, so that way this could basically serve as your timeout. So, if we go here, oh yeah. So it goes to closed because it timed out. And so if we make the timeout a little bit longer, hopefully this answers the question. But, I believe it does, because pre after he'd asked that, John had said, wait, can I just add an after on the state invoking the service?

[25:00] I need to test this, so that's your answer, though. Yeah, yeah, you absolutely, you can, um. And also this is more of an advanced topic which we might do a webinar on, too. But these, whatever we want to call them, like logic creators, like from promise, from transition, from observable, from callback, all of these things are actually really composable. So in the future, you could say something like source from promise with timeout, and you could make your own and so you could do something like that.

[26:00] And maybe this would automatically have some sort of onError that goes somewhere. So yeah, you, you could definitely have custom logic in there, and I've been playing around a lot with that. Just think of things like from undo redo, or from or sorry with undo redo, you know, things like that where you could basically decorate these, actor logic creators, I guess that's what we'll call them, John asks. Anything else new in types aside from services being now named actors?

[27:00] Oh yeah, so that is an important point.

[27:00] Services is renamed to actors, and the idea is that everything is an actor. As far as types, again, we're still working on types. It's a it's a long story to be told, but, yeah, types, there will be some new things. Oh yeah, so I should mention that, uh. We're still working on TypeGen support as well. But I'm going to copy and paste this over just to make this a little bit more clear.

[27:00] Typegen now it's going to belong, I guess that should just be that Typegen is now going to belong inside of that types property. So again, we're no longer using schema, we're now using types, and TypeGen is going to appear here. And then, I guess it's type Gen, regardless, it's going to appear with, oh, it's because it didn't automatically do the type gen thing. I disabled TypeGen in this repo because we have a million machines in here and it would wreak havoc.

[28:00] But in normal projects, you should use Typegen, but yeah, so Typegen would appear right over there. Great. And Cedric asks, does the visualizer work with version 5 or do we have something to set up? Yeah, so the visualizer is, uh. Mainly for XState version four, it does, like with basic machines, work with XState v5. Not all the features are going to be represented in there, and that's because we're still building out support for it.

[28:00] Also, we're focusing our efforts on making the U.M. The Stately editor be the source for inspecting these machines. And so that's going to, you know, be compatible with XState version 4 and version five. So we just released XState version 5 beta. And so that's why, you know, now we're going to be adding all those additional features onto the editor. And, maybe even the visualizer, or maybe we'll have a simplified visualizer, but, those are coming soon.

[29:00] Long story short. Yeah, I mean, let us know what you think about that. I mean, it would be great. I think eventually, the editor is really the way forward. Stately Studio has so much to offer you, and we know that it's not quite the same feature set as the old Viz right now. But if you have any particular priorities or use cases there, you let us know and that will help us know what to prioritize as well.

[29:00] And Cedric also asks, I remember Farzad had talked about adding a sequence diagrams into the visualizer. Do you know if it's still planned? Yep, that's definitely the plan and we are actually working on it. In fact, I got a related message this weekend about it. So, yeah, it is, it is definitely in progress.

[30:00] And yeah, it's going to be much more general purpose than just inspecting machines. So it's going to be a really super powered, sequence diagram. And so that's going to be important. Because in XState V5, any actor is a first class citizen, so it's not, it's. No longer just machine, and then everything else that you can invoke or spawn. But, actually, I should talk about that real quick, too. Let's say that you have some logic in a promise or an observable or something else.

[30:00] In fact, let's make a simple reducer, const counter equals, from transition, and let's say we have saved event. If you've used Redux before, this is going to feel immediately familiar to you. So, if event dot type is increment, then we're going to return state plus one notice how you know, plus one. We're adding plus one to a state. What does that mean? Otherwise, we're going to return the state. You define the initial state in the second argument here.

[31:00] And so this is as basic as you could get with actor logic. So notice, over here, you could think of this as a very, very simple state machine that's just reading and events and updating the states. And it's called from transition, because this is a transition function, it's we. We don't want to use the term reducer because that gets confusing, so here's your transition function. And then, just like with machines, if you wanted to create an actor from this, you could say, const. actor equals, interpret, counter, dot, start, and so again, you could use that normal logic over here.

[31:00] And yeah, it doesn't have to be a machine, so that's one of the big, big new features. There's also, from promise, there's from observable, from event, observable from callback, and of course, there's create machine, which is from a machine. So, yeah, oh nice, little bit of encouragement from Erik. This upgrade is so complicated. On your side, you guys are doing great. And, yeah, I have to say that, uh. David and Mateusz have been like, really, thinking everything through in such detail.

[32:00] And trying to make it as straightforward as possible for all of you to be able to move things over. And if you're if someone was getting started with XState for the first time. So if you're working with teammates who are also you're trying to get them on board. This should also make it easier for them to get started with XState. I'd say I think a lot of the naming is much easier to understand, yeah.

[32:00] Exactly, we're simplifying the naming, trying to simplify a lot of concepts. And just like Erik said he, he said he loves the from stuff. APIs I do, too. I think that there's going to be a lot of superpowers in here. And the excuse of I don't want to convert everything to a state machine or statechart, or I don't want to learn about using state machines or statecharts is no longer a valid excuse.

[33:00] With Exceed version 5, because now you could use whatever you want. Of course, with state machines you're going to have the most flexibility, you're going to be able to invoke, spawn. Other kinds of actors do things like manage effects and things like that, and handle all kinds of events, uh. Whereas things like from promise, from observable, from transition are a little bit more limited. But, you know, most developers write their logic in ways that could be represented by those kinds of logic anyway.

[33:00] So, you know, it all works out, and so on that, John says. So all the from functions return an invocable actor definition. Yeah, so, let me just go back to here. This right now, don't worry about the naming, but it's typed as actor behavior, that's probably going to be renamed to actor logic. But this you could think of as a type of state machine, and so this state machine can be passed into interpret, and this creates an actor, so this creates an actor creator.

[34:00] I guess you could say just what we're going to call it logic, because otherwise it gets really confusing. Yeah, so this, this creates a thing that you could pass to interpret, so that you could start an actor. Yeah, Nick asks. Do these new actors also work with useSelector and useActor? Can we subscribe to their state within our React components? Let's find out. So I have this actor over here, which I'm using, Vite, by the way.

[34:00] So this is just, in global scope on the window. And, I am going to say const count equals useSelector. And I'm going to grab that actor, I'm just going to, well, I guess I could just grab the count. I mean, that's the only state that's available on the actor and so. Let's, let's put it somewhere, okay, let's put it as a big H1 tag over here, so everyone can see it. And this is going to be count, and whenever I click count, I am going to, send type, Inc.

[35:00] But I need to actually send it to that actor, so I'd have to say actor dot send type, Inc. And remember, this is in global scope, ideally, when you're working with reactor other frameworks. You would have this in a more reasonable way, like maybe pass down from a prop or made available through context. But this is going to work as well. That's another big thing that I forgot to mention. One breaking change in XState V5 is that you can no longer send a, a string.

[36:00] You have to send an event so, it will warn you on. Or, sorry, it will throw an error on runtime if you accidentally do this. But in TypeScript, it will warn you anyway. So just make sure to always send an event object. And the same thing applies to any actor, okay, so now we have a big counter over here and we could click and we see it's going to increment. So the short answer is yes.

[36:00] These actors do work with useSelector, and there currently is a PR to make, the actor creators, I guess we'll call it uh. Work inside of use, machine use, interpret as well, if you wanted to, and also the new create actor context API. So yeah, Erik loved sending strings. I said, I've always felt like cheating, but it was so ergonomic. Well, you know what, you could always, you know, just make function, use, Erik machine.

[37:00] And then you have a machine over here, and then you could transform those strings into like. This could easily be done in user land. If you really loved that, you know, that ergonomic, whatever, behavior. Uh. One of the main reasons we did this is because of TypeScript, and so in TypeScript, if you save, this could be a string or an object, and only certain kinds of strings. That made the typings really, really complicated.

[37:00] So, yeah, that's one of the big reasons to change. We wanted, both faster types, and we wanted stricter. Types so that, the typings are more reliable and also in terms of consistency, especially when you're onboarding a new coder to your code base. You don't want them to see. A mix of strings and objects and then having them question, wait, which one is what? And then you having to explain well, the string is sort of a shorthand.

[38:00] But if you want to make that have payload, then you have to convert that to an object like that. That just gets really confusing. So if we default to you could only send objects, then it becomes really easy to see, like, Oh, I see how I could send payload. So yeah, I'll get that topic. I've just been because I'm. I have a checklist of things that we've got in on migration guide that I'm keeping an eye on.

[38:00] We also are for eventless. The always events, now need to be defined through the always property, and you can't define them via an empty string as well. So that's kind of in the related area, isn't it? That's that's exactly correct. So you used to be able to do this, and of course, it's going to yell at you. With maximum call stack size exceeded, which is not the error that we were expecting.

[38:00] But instead of this, and this has been true for V4 for a while. But you should use always instead. And then you could, or whatever. So, yeah, very good point. Erik's, it's also said. I always hated the allowed a string when the event required a payload. So once again, David is not wrong yet. Prove yourself wrong there. Erik [Laughter] exactly so. It's, it's a safety mechanism, it's for your own good. I, I want to really quickly talk about another new feature, too.

[39:00] Now that we got past the migration stuff, more or less, the guard here, let's actually turn this into a, a proper guard. So I'm gonna say feedback is not empty, and then I'm going to specify it in the second argument over here, so this is still guard, feedback is not empty. And then I'm going to check if context.feedback.length is greater than zero, so this should work. Okay, it's because we do have some string in here, oh.

[40:00] That's annoying. Anyway, let's say, though, that, and you've probably done this before if you had a lot of complex guards, but, let's say that we have. A feedback is empty, and so this is just checking if the feedback, that length is zero. And by the way, I'm just going to change this to just an empty string. And by the way, this is another benefit of the input you could override the input, you could validate it, sanitize it, whatever, instead of having it go directly onto the machine.

[40:00] So now it's always going to be empty and this should be invalid. But let's say that we didn't want to create a feedback is not empty guard, just because, you know, we already have this, so what we could do is use a higher order guard. And in XState, we could import not, and then we could pass in that string, so this does exactly what it looks like. We're basically saying, if this guard evaluates to false, which means that not will evaluate to true, then we'll go to the submitting target.

[41:00] So hopefully this logic works. And okay, you could see that it's disabled still until we type something in and then we could submit that after two seconds. And so there's a lot of guards like this, and so let's add another one. Actually, we could also do it in line just to show you the API. So let's say that we wanted to, check two guards, we wanted to check, not. Feedback is empty and I don't know, just something that evaluates to true and we could import.

[42:00] And from XState, or sorry, we don't need I. I did that in the wrong place. I'm just going to inline that over here. We could use the and higher order guard and combine these guards. So, now if we go back, let's make this false. So again, this should evaluate the if I could type should evaluate the false always. So, if I click back, you can see that submit is still disabled. And you could go however deep you want over here, like we could say, and not, or, with the import, import, or, and then feedback is empty, and you know, maybe something else and true, you just you could, really.

[42:00] Really, combine these guards however you want. So, yeah, this makes the guard story a lot more flexible.

[43:00] That's also another one of my favorite features, too, so I wonder if this works? Not sure. Oh yeah, it works. Cool, um. John asked, is there anything written up about how the team envisages the receptionist pattern working? I've sent him a link to the original, the RFC that we had for that. But is there anything else that you want to mention about the receptionist pattern right now? DAVID yeah, sure, so, the receptionist pattern, it actually does work, and we'll probably do that in a different workshop, but just to preview it to you.

[43:00] Let's say that we have a top level invoke here, which, hopefully, you know, you could do top level invokes. They're very powerful, it means you don't have to go into a state to invoke anything. But let's, have some source here, and let's just say, from callback, and this is whatever, so, this is just gonna be a dummy invoke. And this is only to show you, like, how you would use the, the receptionist pattern.

[44:00] When you create a machine and when you interpret it, it creates an implicit system. And so you could register actors at any depth. So if it's an actor which is invoked from another actor, which is spawned from another actor, like, however deep, you could register that actor to the system. And that's by giving it a system ID. And let's just call this notifier, and so what you could do is, let's say that, you know, this is done.

[44:00] We want to target things, but we also want to send something to the notifier, and this could be any machine at any level. We could do is say actions, send to, and then, uh. Well, we have to get it from the system and so this is going to be system.get notifier. And then we could send it. Whatever event we want, so type notified, message, completed or something like that. And so now you could access that actor again from any level, and so that's basically how the receptionist pattern works.

[45:00] I think this question is, did this person says it is very useful, but is? It serializable? Good question. Oh yeah, so that's another big feature. Actors in general, when you invoke or spawn them, as long as they're named like they have an ID, they the actors themselves won't be able to be serialized, however, their states will be. And so that's why when you're rehydrating an actor, you do want to make sure that you're using the same code for them.

[46:00] So that nothing unexpected happens.

[46:00] And we are going to be, actually, we do have documentation on serialization and rehydration. So, I think you're talking about Rehide, sorry, serializing the actors, but you can serialize, the state as well. So, yeah, there's a difference between creating machines that, you know, that work, and then refactoring them so that they become fully serializable. And we will be writing guides on that. Yeah, which reminds me, going back up to either Ivor.

[46:00] Docs when, as we mentioned earlier, there are docs now at Stately..ai / docs / XState-v5. If you go to Stately..ai / docs. There's a little drop down on the top right hand side where you can choose, XState V5. It's also if you're on a narrow browser, it's in the main menu. If you get lost, if you can't find it, it's there as well. Eventually, these will be the main docs. There's been a full rewrite in terms of everything, pretty much everything, actually.

[47:00] So, you can find that there. Yeah, and also, we didn't cover everything in this webinar, so I just want to mention that. There is a migrating from XState v4 to V5 page in the documentation. Which right now outlines all of the, or most, if not all of the breaking changes. And pretty soon we're going to be adding all of the new features, which, they're spread out throughout the docs. So I just have to create a list and create links to them soon.

[47:00] So, I'll, you know what, let me just quickly speed run through these. First of all, instead of machine, capital, machine, I wonder if I could zoom in on this. Probably not. In fact, I think it says, no, yeah, yeah, it's, it's creates machine instead of the machine, so you're no longer importing machine, it's just create machine.

[48:00] And this. Is important, because if you're using ChatGPT to generate your state machine definitions, sometimes it will give you the capital machine. So that's just something to be aware of. We talked about going from Cond to guard, that's this area over here, we also talked about single arguments. Oh, there's this one over here, when you interpret an actor such as this one and you call actor dot, subscribe with. You know, you're going to get its snapshot, this is no longer going to be the, you know, the previous snapshot.

[48:00] Instead, this is going to be sort of a live or hot observable. I think I'm using that term correctly, but basically, this snapshot is not going to come in until a state transition has actually occurred. So if you wanted to get the current snapshot, don't do it in here. Instead, be more explicit. You're going to, current equals actor, you're going to call actor.get snapshot, and that's where you could retrieve it from.

[49:00] So, this makes subscribes behavior more predictable, because you know that you're only going to be getting it when. When they come in, so, see, what else? Oh yeah. So the send action is deprecated, don't use the send action anymore, and this is true for version four as well. So let's say I have actions send, and then you want to send, submit, to somewhere, you know, like that, instead of doing that, you're going to say actions, send to, and that could be somewhere.

[49:00] And then you give your events just like that, and also if you want to send in events to self. The raise action has become a little bit super powered, so now you could say, actions, raise. You know, some events, so type, submit or some event. And you could also specify things like a delay. You could give it an ID if you want to, so you have all those things available to you.

[50:00] This should come from XState. Of course, that's not a valid event. So yeah, so yeah, there's that. I think the next thing on the list is external transitions. Oh yeah, honestly, that has always confused me as well. So let's say that we have an internal, you know, on do something. Whatever, if you didn't want to exit and re-enter the state, before you had to do something like internal true, which was a little bit confusing.

[51:00] But what you really want to say is I don't want to re-enter the state that I'm in. So instead, you say, re-enter, false, so that will prevent you from well from re-entering the state. So it's a lot clearer than the internal false, etc. So if you wanted this to be an internal transition, or, sorry, an external transition, you could say re-enter. True. Yeah, that's that change. Just scrolling here and seeing what else.

[51:00] I'm also happy to answer any questions. We've. We have got some more questions I can jump back to. First up is from, Kevin says. Can a parent machine still pass a data function prop down to an invoked actor, or is there a more input like API for that? Oh, great question, Okay, so let's see that we're invoking something like this from promise. Actually, this is a very good example, so I'm going to go back here.

[52:00] In fact, yeah, we have a little bit of time. Let me just make this an example. And I might have to undo a whole bunch of stuff that I did, or give me just one second while we do a little. In fact, I could probably just delete this. Hopefully everything still works. Let's see. Nope, that's not where I want to go. So that submits submitting and it says, thank you for your feedback.

[52:00] Let's make this actually come from the, you know, the submit feedback actor. So we have target thanks and actions, or actually no. Just to save a little bit on time, let's say that we actually want to provide that feedback into submit feedback. So obviously there's not like a the old way of doing it, you have to pass it from here, etc. There's no longer anything like that. So instead, what we have to do is provide input over here, so input.

[53:00] And we could just give it the feedback like that. And so this is, right now we have to fix the types on this. But this is going to come from, you know, context or event or anything else, and. This is going to be passed in to this promise. So how exactly does that work? Well? In the initial argument over here, we have an object and that object takes in that input. So in this case, the input is just going to be a string, which is context.feedback.

[53:00] And so we could grab that input in from promise and give it directly to here. Now, the one of the biggest reasons that we're doing this is because, this now becomes completely flexible. So I could just say, like, feedback submitter, and now all of a sudden, oops, I don't know what language I was writing there. Accounts feedback submitter. We get to say, hey, this is just a thing that you could use absolutely anywhere.

[54:00] And this thing only cares about, like, the input being a certain type, so it's not tied to any specific machine, which makes it a lot more flexible. Again, there's some typing work we have to do to clean that up. But now you could just pass that in pretty much anywhere and invoke that from any actor, and so the actors are more loosely coupled. So, that's how you would do that.

[55:00] Great. Nick asks. Can we access invoked actors within child machines from a parent machine? Yeah, of course, and so the way you would do that, this is exactly the same as before. Let's say that you have, you know, state dot, context dot some actor, you know, dot some other actor, you know, etc. Or, like context thought some other actor. Like. This remains exactly the same. Whether you're using V4 or V5, so yeah.

[55:00] However, if you're using the receptionist pattern, I believe then I'll have to double check this again. Brand new APIs, uh. But you could also just say const some actor ref equals actor dot, system dot, get, and then you could get the name of that. So now it makes it a lot easier to access actors that are registered with the system with a system ID from anywhere in that system. Tree or system.

[55:00] Family. Whatever we want to call that great, we've only got a few minutes left. Of any questions you want to ask, make sure that you ask us, on our social media platforms, or in, in our. Discord or here in the comments. We've got a few more things that are on our list that we haven't mentioned. David so I don't know if we just want to rattle through them very quickly. Let's do it.

[56:00] First up, uh. Strict mode versus wild card transitions Oh yeah, so there is no longer a strict mode, there is no. You know, you might have been used to writing strict.true or strict true. This was in the early days of XState, and so some of you might still be doing this. The main purpose of using strict true is to prevent any unwanted events, from coming in. So instead, I would highly recommend again, to be more explicit in on the root, the root state.

[57:00] Provide a wild card and just do something. You know, handle it, throw in error, do whatever you want to, you know, send it off somewhere. But this is where any unknown events would go. And so, yeah, that's how you would handle that great. State dot history property being removed, yeah, so state dot history, again, it's no longer there, and that's because number one, it was like a memory leak nightmare, which we did solve.

[57:00] But that also meant that, the states were truncated, we were mutating state in order to. Well, to prevent accidentally creating a blockchain in your application, which, you know, we don't want, no one wants that. So instead, the right thing to do is to, oh, handle it yourself. So you could do something like, let, uh. Prev state equals actor dot get snapshot and then whenever you subscribe, so let's say actor.subscribe state. We could just like, let's say you have a comparison function like compare, create state, state.

[58:00] Essentially, you already have the previous state over here, and then you could assign, previous state equals states. So it takes two lines to do this. Basically obviating the need for something like state.history, and in general, we're trying to make the state object a lot simpler. So, actor dot batch method that's been removed, right? Yeah, so instead of that, let's say we have event equals to this, you just do for, const event of events and then actor dot send event.

[58:00] So this is how. You do that, so, yeah, this is your basic way of batching events, or batch sending events. Great, uh. Going on to, transitions and guards, and forgive me if I think I don't think you mentioned this earlier. So, rather than using in, the transition property, we want folks to use guard state in. Yeah, that's correct. So, just as another refresher, let's make this. This is impractical. Well, let's make this a parallel state.

[59:00] You know, you would have, like some states, let's say one and two, and let's say that you want to make sure that you were in states two or something. Let's just give this an idea of two. So, instead of doing in, which was a little bit confusing, it looks built into the transition. You would use a guard, because that's essentially what it is, it's a special kind of guard. So you could say on some event, instead of in two, for instance, you would say, guard, state in.

[59:00] And so this allows you to just ensure that you're in pretty much any state in the machine. So if you want to make sure that we're in submitting, for instance, we could just say submitting or form or something, or if we were in a nested state, you know, we could do, form error. Of course, there is state. I was gonna say that they're saying not in, but now you could just do state in with the not higher level guard, and that works as well.

[1:00:00] Fabulous. And finally, staying on guards and the params property that you can use on action and guard objects, right? Yeah, so, before let's add an action over here, actions type. Do something like, let's say, we want to send a message. Before in XState before, you, you would put the message directly on the object, so hello world, um. But now it would be nested within params, and this is again to simplify the typings and to just uh.

[1:01:00] It also provides a standard for visualization because we could just grab the parameters from that params object too. Guards work the same way, so if you have a, a guard over here, so guard, you could say type is greater than. And params, value 10, for instance, so they have the same shape. Great. Now I don't think we have any more questions. I think we have managed to wrap up everything that is currently in the migration guide.

[1:01:00] I mean, chances are there will be more added there in the future.

[1:02:00] And that is a reminder that if you want to check out that guide, go to Stately..ai. Docs Slash XState Dash V5, and there is a migrate to V5 page in there. Where. And we'll put the link in the description to the video later on, and I suppose, before we end up making this incredibly long. If you do have any more questions, do feel free to let us know in our Discord on any of our social media platforms.

[1:02:00] We will get back to you as quickly as we can. Or we have another office hours coming up this Friday. And you're welcome to join us there, where you can ask your questions live, and we can give you live demos as well. So be sure to join us because office hours is always great fun. Mm-hmm. Anything else that you wanted to say, David, about V5 at this point? Yeah. So we are going to be in beta for a while.

[1:03:00] We definitely want people to kick the tires and try it out. It's using basically the same tests as before, so it's been extensively tested. And so, consider V5 in terms of functionality, a superset of V 4. If you're concerned about things like, is this going to? Well, obviously, there might be some breaking changes. But are there going to be things that are no longer possible in V5, that were possible in V4?

[1:03:00] And the chances are likely not. In fact, you're probably going to be able to do more things and have more patterns available to you in V5. So we'll be making an announcement about V5 beta, later this week, so definitely share it around. And yeah, I'm also going to be. Or actually. We're also going to be updating the documentation as questions come in, just to make sure that we're answering all the questions and clarifying all of the concepts.

[1:03:00] Yeah, so please just speak to us, just let us know what you need, we're doing. Our best. But we actually really thrive off having your feedback because that helps us know what direction we need to go in, what questions we need to answer. So thank you everyone for joining us. We really appreciate it. We really appreciate all the questions and everyone joining in the comments. It's always much better when you're here.

[1:04:00] And we may do another one of these if you would find it useful. So please let us know. There are any other examples you would like to see? Any other demos? Just give us a shout. Thank you everyone for joining us and we will speak to you soon. Thank you all, bye-bye.
