User interfaces can be expressed by two things:
- The state of the UI
- Actions that can change that state
From credit card payment devices and gas pump screens to the software that your company creates, user interfaces react to the actions of the user and other sources and change their state accordingly. This concept isn’t just limited to technology, it’s a fundamental part of how everything works:
For every action, there is an equal and opposite reaction.
– Isaac Newton
This is a concept we can apply to developing better user interfaces, but before we go there, I want you to try something. Consider a photo gallery interface with this user interaction flow:
- Show a search input and a search button that allows the user to search for photos
- When the search button is clicked, fetch photos with the search term from Flickr
- Display the search results in a grid of small sized photos
- When a photo is clicked/tapped, show the full size photo
- When a full-sized photo is clicked/tapped again, go back to the gallery view
Now think about how you would develop it. Maybe even try programming it in React. I’ll wait; I’m just an article. I’m not going anywhere.
Finished? Awesome! That wasn’t too difficult, right? Now think about the following scenarios that you might have forgotten:
- What if the user clicks the search button repeatedly?
- What if the user wants to cancel the search while it’s in-flight?
- Is the search button disabled while searching?
- What if the user mischievously enables the disabled button?
- Is there any indication that the results are loading?
- What happens if there’s an error? Can the user retry the search?
- What if the user searches and then clicks a photo? What should happen?
These are just some of the potential problems that can arise during planning, development, or testing. Few things are worse in software development than thinking that you’ve covered every possible use case, and then discovering (or receiving) new edge cases that will further complicate your code once you account for them. It’s especially difficult to jump into a pre-existing project where all of these use cases are undocumented, but instead hidden in spaghetti code and left for you to decipher.
Stating the obvious
What if we could determine all possible UI states that can result from all possible actions performed on each state? And what if we can visualize these states, actions, and transitions between states? Designers intuitively do this, in what are called “user flows” (or “UX Flows”), to depict what the next state of the UI should be depending on the user interaction.

In computer science terms, there is a computational model called finite automata, or “finite state machines” (FSM), that can express the same type of information. That is, they describe which state comes next when an action is performed on the current state. Just like user flows, these finite state machines can be visualized in a clear and unambiguous way. For example, here is the state transition diagram describing the FSM of a traffic light:

What is a finite state machine?
A state machine is a useful way of modeling behavior in an application: for every action, there is a reaction in the form of a state change. There’s 5 parts to a classical finite state machine:
- A set of states (e.g.,
idle
,loading
,success
,error
, etc.) - A set of actions (e.g.,
SEARCH
,CANCEL
,SELECT_PHOTO
, etc.) - An initial state (e.g.,
idle
) - A transition function (e.g.,
transition('idle', 'SEARCH') == 'loading'
) - Final states (which don’t apply to this article.)
Deterministic finite state machines (which is what we’ll be dealing with) have some constraints, as well:
- There are a finite number of possible states
- There are a finite number of possible actions (these are the “finite” parts)
- The application can only be in one of these states at a time
- Given a
currentState
and anaction
, the transition function must always return the samenextState
(this is the “deterministic” part)
Representing finite state machines
A finite state machine can be represented as a mapping from a state
to its “transitions”, where each transition is an action
and the nextState
that follows that action. This mapping is just a plain JavaScript object.
Let’s consider an American traffic light example, one of the simplest FSM examples. Assume we start on green
, then transition to yellow
after some TIMER
, and then RED
after another TIMER
, and then back to green
after another TIMER
:
const machine = {
green: { TIMER: 'yellow' },
yellow: { TIMER: 'red' },
red: { TIMER: 'green' }
};
const initialState = 'green';
A transition function answers the question:
Given the current state and an action, what will the next state be?
With our setup, transitioning to the next state based on an action (in this case, TIMER
) is just a look-up of the currentState
and action
in the machine
object, since:
machine[currentState]
gives us the next action mapping, e.g.:machine['green'] == {TIMER: 'yellow'}
machine[currentState][action]
gives us the next state from the action, e.g.:machine['green']['TIMER'] == 'yellow'
:
// ...
function transition(currentState, action) {
return machine[currentState][action];
}
transition('green', 'TIMER');
// => 'yellow'
Instead of using if/else
or switch
statements to determine the next state, e.g., if (currentState === 'green') return 'yellow';
, we moved all of that logic into a plain JavaScript object that can be serialized into JSON. That’s a strategy that will pay off greatly in terms of testing, visualization, reuse, analysis, flexibility, and configurability.
See the Pen Simple finite state machine example by David Khourshid (@davidkpiano) on CodePen.
Finite State Machines in React
Taking a look at a more complicated example, let’s see how we can represent our gallery app using a finite state machine. The app can be in one of several states:
start
– the initial search page viewloading
– search results fetching viewerror
– search failed viewgallery
– successful search results viewphoto
– detailed single photo view
And several actions can be performed, either by the user or the app itself:
SEARCH
– user clicks the “search” buttonSEARCH_SUCCESS
– search succeeded with the queried photosSEARCH_FAILURE
– search failed due to an errorCANCEL_SEARCH
– user clicks the “cancel search” buttonSELECT_PHOTO
– user clicks a photo in the galleryEXIT_PHOTO
– user clicks to exit the detailed photo view
The best way to visualize how these states and actions come together, at first, is with two very powerful tools: pencil and paper. Draw arrows between the states, and label the arrows with actions that cause transitions between the states:
We can now represent these transitions in an object, just like in the traffic light example:
const galleryMachine = {
start: {
SEARCH: 'loading'
},
loading: {
SEARCH_SUCCESS: 'gallery',
SEARCH_FAILURE: 'error',
CANCEL_SEARCH: 'gallery'
},
error: {
SEARCH: 'loading'
},
gallery: {
SEARCH: 'loading',
SELECT_PHOTO: 'photo'
},
photo: {
EXIT_PHOTO: 'gallery'
}
};
const initialState = 'start';
Now let’s see how we can incorporate this finite state machine configuration and the transition function into our gallery app. In the App
‘s component state, there will be a single property that will indicate the current finite state, gallery
:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
gallery: 'start', // initial finite state
query: '',
items: []
};
}
// ...
The transition
function will be a method of this App
class, so that we can retrieve the current finite state:
// ...
transition(action) {
const currentGalleryState = this.state.gallery;
const nextGalleryState =
galleryMachine[currentGalleryState][action.type];
if (nextGalleryState) {
const nextState = this.command(nextGalleryState, action);
this.setState({
gallery: nextGalleryState,
...nextState // extended state
});
}
}
// ...
This looks similar to the previously described transition(currentState, action)
function, with a few differences:
- The
action
is an object with atype
property that specifies the string action type, e.g.,type: 'SEARCH'
- Only the
action
is passed in since we can retrieve the current finite state fromthis.state.gallery
- The entire app state will be updated with the next finite state, i.e.,
nextGalleryState
, as well as any extended state (nextState
) that results from executing a command based on the next state and action payload (see the “Executing commands” section)
Executing commands
When a state change occurs, “side effects” (or “commands” as we’ll refer to them) might be executed. For example, when a user clicks the “Search” button and a 'SEARCH'
action is emitted, the state will transition to 'loading'
, and an async Flickr search should be executed (otherwise, 'loading'
would be a lie, and developers should never lie).
We can handle these side effects in a command(nextState, action)
method that determines what to execute given the next finite state and action payload, as well as what the extended state should be:
// ...
command(nextState, action) {
switch (nextState) {
case 'loading':
// execute the search command
this.search(action.query);
break;
case 'gallery':
if (action.items) {
// update the state with the found items
return { items: action.items };
}
break;
case 'photo':
if (action.item) {
// update the state with the selected photo item
return { photo: action.item };
}
break;
default:
break;
}
}
// ...
Actions can have payloads other than the action’s type
, which the app state might need to be updated with. For example, when a 'SEARCH'
action succeeds, a 'SEARCH_SUCCESS'
action can be emitted with the items
from the search result:
// ...
fetchJsonp(
`https://api.flickr.com/services/feeds/photos_public.gne?lang=en-us&format=json&tags=${encodedQuery}`,
{ jsonpCallback: 'jsoncallback' })
.then(res => res.json())
.then(data => {
this.transition({ type: 'SEARCH_SUCCESS', items: data.items });
})
.catch(error => {
this.transition({ type: 'SEARCH_FAILURE' });
});
// ...
The command()
method above will immediately return any extended state (i.e., state other than the finite state) that this.state
should be updated with in this.setState(...)
, along with the finite state change.
The final machine-controlled app
Since we’ve declaratively configured the finite state machine for the app, we can render the proper UI in a cleaner way by conditionally rendering based on the current finite state:
// ...
render() {
const galleryState = this.state.gallery;
return (
<div className="ui-app" data-state={galleryState}>
{this.renderForm(galleryState)}
{this.renderGallery(galleryState)}
{this.renderPhoto(galleryState)}
</div>
);
}
// ...
The final result:
See the Pen Gallery app with Finite State Machines by David Khourshid (@davidkpiano) on CodePen.
Finite state in CSS
You might have noticed data-state={galleryState}
in the code above. By setting that data-attribute, we can conditionally style any part of our app using an attribute selector:
.ui-app {
// ...
&[data-state="start"] {
justify-content: center;
}
&[data-state="loading"] {
.ui-item {
opacity: .5;
}
}
}
This is preferable to using className
because you can enforce the constraint that only a single value at a time can be set for data-state
, and the specificity is the same as using a class. Attribute selectors are also supported in most popular CSS-in-JS solutions.
Advantages and resources
Using finite state machines for describing the behavior of complex applications is nothing new. Traditionally, this was done with switch
and goto
statements, but by describing finite state machines as a declarative mapping between states, actions, and next states, you can use that data to visualize the state transitions:

Furthermore, using declarative finite state machines allows you to:
- Store, share, and configure application logic anywhere – similar components, other apps, in databases, in other languages, etc.
- Make collaboration easier with designers and project managers
- Statically analyze and optimize state transitions, including states that are impossible to reach
- Easily change application logic without fear
- Automate integration tests
Conclusion and takeaways
Finite state machines are an abstraction for modeling the parts of your app that can be represented as finite states, and almost all apps have those parts. The FSM coding patterns presented in this article:
- Can be used with any existing state management setup; e.g., Redux or MobX
- Can be adapted to any framework (not just React), or no framework at all
- Are not written in stone; the developer can adapt the patterns to their coding style
- Are not applicable to every single situation or use-case
From now on, when you encounter “boolean flag” variables such as isLoaded
or isSuccess
, I encourage you to stop and think about how your app state can be modeled as a finite state machine instead. That way, you can refactor your app to represent state as state === 'loaded'
or state === 'success'
, using enumerated states in place of boolean flags.
Resources
I gave a talk at React Rally 2017 about using finite automata and statecharts to create better user interfaces, if you want to learn more about the motivation and principles:
Slides: Infinitely Better UIs with Finite Automata
Here are some further resources:
- Pure UI by Guillermo Rauch
- Pure UI Control by Adam Solove
- Wikipedia: Finite State Machines
- Statecharts: A Visual Formalism for Complex Systems by David Harel (PDF)
- Managing State in JavaScript with State Machines by Krasimir Tsonev
- Rambling Thoughts on React and Finite State Machines by Ryan Florence
Great article. But will the average imperative programmer ever see the light? If they read your excellent work they probably would and then get criticism for taking too long. Such is life
Thanks! I’d argue that the above code is even shorter and more concise than typical imperative code. And you spend less time coding because you don’t have to worry about reaching impossible states.
I used stately.js (https://github.com/fschaefer/Stately.js) with my last project, did a lot of the heave lifting for me.
Awesome article, and really like the CSS ideas. I’ve found that leveraging elm’s union types to write FSM has become an intuitive way to use css animations. Never considered bringing it into a js only environment. Makes me want to get back into react. Interesting idea to use data-attributes.
I actually built things in JS using a self-made library using similar concepts obtained from studying finite automata. I found that, though it made things perhaps more clear, it was very dependent on what I was building and could seem a bit overwhelming when it came to bigger UI implementations.
The pros for me were that it was super easy to map out the states/transitions of applications, and I did think that was pretty eye-opening when it came to application state management.
Excellent article though! I think it’s definitely something that should be looked into by more Web Devs and, if they’re anything like me, they’ll probably find this much more palatable than the usual obscenely gaudy and masturbatory explanations of FSA that plague the internet and textbooks.
FSMs (and statecharts) should never be completely dependent on the UI that is being built. The UI should just be a layer that the user can interface with that can dispatch actions, and the FSM should only be responsible for determining the next state based on those actions and the current state.
For bigger UI implementations, I highly encourage you to read about statecharts, which are an extension of finite state machines, and solve many of the scaling problems that you will encounter with FSMs and complex UIs with greater states.
I’ll be writing an article about statecharts soon!
Instead of change the “loading” property on SUCCESS or event on ERROR, you can use another .then(), after .catch() and set the “loading” property as false. It is just SoC and DRY patterns. :)
Can you clarify? There is no “loading” property; that’s the whole point. You should enumerate the finite states instead of having a collection of boolean flags in your app.
By having boolean flags, such as “loading”, you technically have 2^n possible states (where “n” is the number of boolean flags). Then, it is up to you, the developer, to make sure that a large subset of those 2^n states are impossible, by doing silly things such as “if (!loading && success && !canceled)”, which becomes quickly unmaintainable.
FSMs fully embody SoC and DRY. Imperative programming does not.
Best JavaScript article in a long time. Bring back the FSM. Thank you!
Excellent article. The OMG defined the semantics for FSM’s very well here at http://www.omg.org/spec/PSSM
A very nice tool for developing FSM’s and and any UML is http://staruml.io/
Once could also generate the code for these from this tool.
Caution!!!! Once you understand FSM’s you will fall in love.
Great article. You say this can be applied to MobX stores; would be great to see a mini / follow-on article.
Is there gonna be REDUX example? Would be sweet.
Hi, great article! What is the purpose of style={{‘–i’: i}}
Good question! That’s an inline CSS Variable, now supported in the latest versions of React.