Simple, perfectly timed loading states with
Waitency.

Waitency helps you to create smooth loading experiences for your components.

Define a timeline with a simple typescript object and Waitency will do the rest.

myapp.com/dashboard

The demo is about to start…

0ms1.5s3.4s
Waiting for request…

How it works

You describe the timeline. Waitency runs it.

1

Define your phases

Give each phase a name and a duration. Waitency uses this to know when to transition.

2

Wrap your async work

Waitency tracks the operation and advances through your phases based on elapsed time.

3

Map phases to UI

In your component, read the current phase and decide what to render — nothing, a spinner, a skeleton, a message.

Use cases

Built for real loading scenarios.

Waitency handles the timing so you can focus on the UI. Here are the core patterns it supports.

01

Multi-stage loading feedback

For longer operations, transition through phases — from nothing, to a spinner, to a reassurance message — so users always know something is happening.

02

Prevent loading flash

Minimum display durations keep a spinner or skeleton on screen long enough to feel intentional, even when data arrives a moment after it appears.

03

Graceful error handling

When a request fails, the timeline stops and surfaces the error. Show a retry prompt or fallback UI based on which phase the failure occurred in.

Simple API

One hook.
That's it.

Waitency's flexible timeline API works with any data fetching library and can be used to design any type of component loading experiences.

  • Skeleton, spinners or messages
  • Multi-stage reassurance for long processes
  • Fallback messages for when things aren't running as fast as expected
example.tsx
import { useWaitency, defineTimeline } from '@waitency/react'

const phases = defineTimeline([
  { name: 'silent',  duration: 50  },
  { name: 'spinner', duration: 950 },
  { name: 'message' },
])

function UserProfile({ userId }: { userId: string }) {
  const { phase, start, resolve } = useWaitency({ phases })

  async function load() {
    start()
    const data = await fetchUser(userId)
    resolve(data)
  }

  if (phase?.name === 'spinner') return <Spinner />
  if (phase?.name === 'message') return <Message text="Hang tight…" />
  return <ProfileCard />
}

Get Started

Supported Frameworks

Start with the React package today. More frameworks coming soon.

React

@waitency/react

Get started →

More frameworks coming soon

Install

Ship calmer loading states.

Start with the React package, wire a short timeline into one async interaction, and validate the feel before you roll it across the product.

$npm install @waitency/react
Read the docs →