Need private packages and team management tools?Check out npm Teams »


1.1.4 • Public • Published


A react component for visual indication of async state.

success animation error animation

Wrap it around some component that has a callback. If the callback returns a promise when invoked, the wrapped component receives props that indicate the current state of the promise.

See also:

Note: This is a rewrite of @loopmode/async-state using hooks. Thus, it requires react@16.8.0 or higher. If you need the functionality for older react versions, check out @loopmode/async-state


Using npm:

npm install --save @loopmode/stateful

Using yarn:

yarn add @loopmode/stateful


In the example below, we use <Stateful> without any props.

The default values are used, and the button

  • has a disabled prop when clicked, until the promise is resolved or rejected
  • has a busy CSS class after busyDelay milliseconds (default 0 - right away)
  • has a success CSS class for successDuration milliseconds (default 1000) when the promise is resolved
  • has an error CSS class for errorDuration milliseconds (default 1000) when the promise is rejected
import React from 'react';
import ReactDOM from 'react-dom';
import Stateful from '@loopmode/stateful';
function handleClick() {
    return fetch('');
const Demo = () => (
        <button onClick={handleClick}>load</button>
ReactDOM.render(<Demo />, document.getElementById('root'));

Supported props


Prop Type Default value Description
general props
callbacks PolyType ['onClick'] Names of callbacks to intercept and check for promises
hintDuration Number 1000 Duration in milliseconds for both Status.SUCCESS and Status.ERROR - outweighed by successDuration and errorDuration
delimiter String ' ' Delimiter for splitting PolyType props of type String into multiple values
rejectValue Function value => value instanceof Error Whether to indicate Status.ERROR for a promise that was actually resolved with a value
for pending
pendingProps PolyType ['disabled'] Names of props to add for Status.PENDING
pendingClasses PolyType [] Names of CSS classes to add for Status.PENDING
for busy
busyProps PolyType ['disabled'] Names of props to add for Status.BUSY
busyClasses PolyType [] Names of CSS classes to add for Status.BUSY
busyDelay Number 0 Duration in milliseconds to wait after Status.PENDING and before Status.BUSY
for error
errorProps PolyType [] Names of props to add for Status.ERROR
errorClasses PolyType ['error'] Names of CSS classes to add for Status.ERROR
errorDuration Number undefined Duration in milliseconds for Status.ERROR - outweighs hintDuration
for success
successProps PolyType [] Names of props to add for Status.SUCCESS
successClasses PolyType ['success'] Names of CSS classes to add for Status.SUCCESS
successDuration Number undefined Duration in milliseconds for Status.SUCCESS - outweighs hintDuration

PolyType props

The values of PolyType props may be of type String, Array or Function. Here are some examples:

import Stateful, { Status } from '@loopmode/stateful';
// String
<Stateful pendingProps="disabled" />
<Stateful pendingProps="disabled pending" />
<Stateful pendingProps="disabled,pending" delimiter="," />
// Array
<Stateful pendingProps={['disabled']} />
<Stateful pendingProps={['disabled', 'pending']} />
<Stateful pendingProps={myPropsArray} />
// Function
<Stateful pendingProps={() => ({ variant: 'loading' })} />
<Stateful pendingProps={(status) => ({ isLoading: status === Status.BUSY })} />

Status value

When you provide a function to a PolyType prop, it will be invoked with the current status and should return a props object.

status value Description
Status.IDLE 0 The default state - no props are added to wrapped children
Status.PENDING 1 A callback was invoked, and it returned a promise. Wrapped children now receive pendingProps and pendingClasses
Status.BUSY 2 The returned promise has been pending for more than busyDelay milliseconds. Wrapped children receive busyProps and busyClasses
Status.SUCCESS 3 The returned promise was resolved. Wrapped children receive successProps and successClasses for successDuration milliseconds
Status.ERROR 4 The returned promise was rejected. Wrapped children receive errorProps and errorClasses for errorDuration milliseconds

You can either check for the values inline or import the Status module:

// either as wildcard import from the specific Status module:
// import * as Status from '@loopmode/stateful/lib/Status';
// or as named import from the main module:
import { Status } from '@loopmode/stateful';
console.log({ Status });

Usage with UI libraries

Most UI libraries and frameworks come prepared for these situations and provide class names or props to make a button look green or red or busy. While @loopmode/stateful makes it easy to write a custom wrapper for any library you use, it comes with a couple of presets for popular frameworks. (Suggestions and especially pull requests for more support are highly welcome!)

To use the pre-configured wrapper components, you should import them specifically from lib/wrapper. Ideally, you would do this only once for the local Stateful component of a project, configure it there, and import that one across your codebase.

name examples implementation usage
antd example wrappers/antd.js import Stateful from '@loopmode/stateful/lib/wrappers/antd';
bootstrap example wrappers/bootstrap.js import Stateful from '@loopmode/stateful/lib/wrappers/bootstrap';
semantic-ui example wrappers/semantic-ui.js import Stateful from '@loopmode/stateful/lib/wrappers/semantic-ui';
material-ui example - -

Recommended usage

You might be able to use one of the pre-configured library wrappers, but chances are you'll need your own wrapper.

Typically, you should create a local components/Stateful component, configure it once for the needs of your project:

// src/components/Stateful/index.js
import React from 'react';
import DefaultStateful from '@loopmode/stateful';
const CustomStateful = props => {
    return (
            // whatever suits your project
export default CustomStateful;

Check out the default wrappers for some examples using this approach.


npm i @loopmode/stateful

DownloadsWeekly Downloads






Unpacked Size

56.3 kB

Total Files


Last publish


  • avatar