Skip to content

Repository files navigation

status-ink

Animated status and progress text for React, Vue, and other interfaces. Status Ink provides framework-free primitives plus native adapters for React, Vue, and Svelte.

An assistant message moving from checking release configuration through review and completion states.

Install and render

npm install @status-ink/react
import { StatusInk } from '@status-ink/react';
import '@status-ink/react/style.css';

export function LoadingStatus({ status }: { status: string }) {
  return <StatusInk status={status} />;
}

Live demo · TypeScript example · React example · Vue example · Svelte example

Packages

Status labels are caller-owned. Status Ink animates the text you provide; it does not ship a built-in loading-copy catalog or replace real application progress state.

Controlled status changes

Use a controlled transition when your app owns the current state:

import { startStatusTransition } from '@status-ink/core';

const transition = startStatusTransition({ status: 'Queued' });
const unsubscribe = transition.subscribe((display) => console.log(display));

transition.update('Running');
transition.update('Done');

unsubscribe();
transition.stop();
await transition.done;

The initial status renders directly by default. Set initial: 'enter' to animate it on mount. Newer updates cancel stale frames and transition from the latest visible text.

Autonomous fallback statuses

Use the animator when real progress updates are unavailable:

import { startStatusAnimator } from '@status-ink/core';

const animator = startStatusAnimator({
  statuses: ['Loading', 'Indexing', 'Rendering'],
  holdMs: 2500,
  effectSwitchEvery: 2,
  seed: 'loading-panel',
});
const unsubscribe = animator.subscribe((display) => console.log(display));

// When the owning task or view ends:
unsubscribe();
animator.stop();
await animator.done;

order: 'provided' begins with the first supplied status. The default seeded order varies the starting point repeatably. speed changes effect-frame and adapter-shimmer playback but does not change holdMs.

Framework adapters

All adapters support controlled status, autonomous statuses, speed, effects, reduced motion, shimmer, and opt-in live-region announcements.

React

import { StatusInk } from '@status-ink/react';
import '@status-ink/react/style.css';

<StatusInk ariaLive="polite" status="Building" />;
<StatusInk holdMs={2000} statuses={['Loading', 'Indexing', 'Ready']} />;

Use useStatusInk(...) when you want to own the rendered markup.

Vue

<script setup lang="ts">
import { ref } from 'vue';
import { StatusInk } from '@status-ink/vue';
import '@status-ink/vue/style.css';

const status = ref('Building');
</script>

<template>
  <StatusInk aria-live="polite" :status="status" />
</template>

Use useStatusInk(...) when you want to own the rendered markup.

Svelte

<script lang="ts">
  import { StatusInk } from '@status-ink/svelte';
  import '@status-ink/svelte/style.css';

  let status = $state('Building');
</script>

<StatusInk aria-live="polite" {status} />

Use useStatusInk(() => ({ status })) when you want to own the rendered markup.

Effects and timing

Named built-in effects include binary, braille, decode, dna, glitch, hacker, ink, morph, morse, redacted, shuffle, spotlight, typewriter, and wipe. Import a named effect or pass the ordered effects array to use the full catalog.

import { glitchEffect, startStatusTransition } from '@status-ink/core';

startStatusTransition({
  status: 'Loading',
  effects: [glitchEffect],
  speed: 1.5,
});

Effects receive grapheme-aware TextEffectInput values, so emoji, flags, and combining marks are not split in the middle of an animation.

Accessibility and reduced motion

Adapter text is quiet to assistive technology by default. Set ariaLive="polite" in React or aria-live="polite" in Vue and Svelte only when meaningful status changes should be announced. The adapters keep animated visual frames separate from stable live-region announcements.

Reduced motion defaults to auto in framework adapters and follows prefers-reduced-motion. It preserves status changes while disabling text effects and shimmer. The framework-free package accepts the resolved boolean through reducedMotion.

Shimmer can be disabled independently and themed with CSS custom properties:

.status-ink {
  --status-ink-shimmer-duration: 2.5s;
  --status-ink-shimmer-width: 35%;
  --status-ink-shimmer-highlight-color: currentColor;
}

Compatibility

The public packages are ESM and support Node 18 or newer for direct Node usage. Browser consumers should use a modern ESM bundler. React supports React and React DOM 18–19, Vue supports 3.5, and Svelte supports 5. TypeScript is not a runtime dependency.

Development

Use Node ^20.19.0, ^22.18.0, or >=24.11.0 for this repository.

npm install
npm run check
npm test
npm run build

Run npm run dev for the product demo. See CONTRIBUTING.md for contribution guidance and SECURITY.md for vulnerability reporting.

License

MIT

About

Animated status and progress text for React, Vue, and other interfaces.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages