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.
npm install @status-ink/reactimport { 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
@status-ink/core: framework-free status text primitives.@status-ink/react: React component and hook.@status-ink/vue: Vue component and composable.@status-ink/svelte: Svelte 5 component and rune helper.
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.
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.
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.
All adapters support controlled status, autonomous statuses, speed, effects, reduced motion,
shimmer, and opt-in live-region announcements.
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.
<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.
<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.
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.
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;
}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.
Use Node ^20.19.0, ^22.18.0, or >=24.11.0 for this repository.
npm install
npm run check
npm test
npm run buildRun npm run dev for the product demo. See CONTRIBUTING.md for contribution
guidance and SECURITY.md for vulnerability reporting.