loadersz-core README

The project's README from github.com/lumberjacque/loadersz-core. As one markdown file: README.md.

loadersz

npm version npm downloads CI bundle size single loader size motion states license TypeScript core dependencies

Framework-agnostic, dependency-free Canvas loaders for the browser. loadersz ships as a native Web Component and a small imperative TypeScript API-no React, SVG filters, or runtime dependencies.

150 motion states. One fixed state is 2.5–3.1 kB gzipped; the complete switchable library is available from one import.

Install

npm install loadersz

Enumerate available states

Use the tiny, side-effect-free states entry when building a picker or documentation UI:

import { LOADER_STATES } from "loadersz/states";

console.log(LOADER_STATES); // ["working", "searching", …]

Use the category metadata when building a picker or gallery. It keeps product-style loaders, loaders, systems, data & telemetry, reasoning, atmosphere and motion studies in the same order as the official website:

import { LOADER_CATEGORIES } from "loadersz/states";

for (const category of LOADER_CATEGORIES) {
  console.log(category.label, category.states);
}

Import once in your application to register the custom element safely:

import "loadersz";

Custom element

<loadersz-loader
  state="calibrating"
  size="144"
  speed="1.15"
  density="1"
  theme="dark"
  aria-label="Calibrating"
></loadersz-loader>

size is required in practice: it is the square canvas side in CSS pixels. Use 64 for a compact status indicator, 144 for a card, or 280 for a hero-sized loader.

Attributes

Attribute Type Default Description
state state name working Selects the visual geometry.
size number 96 Square side in CSS pixels; values below 16 are clamped.
speed number 1 Timeline multiplier.
density number 1 Geometry detail; clamped to 0.35–2.
particle-radius number 1 Multiplies visible particle thickness; clamped to 0.5–2.5.
theme auto, dark, light auto Canvas colour scheme.
hue 0–360 unset Overrides a mode's native colour palette.
color CSS colour string unset Overrides hue; accepts hex, CSS colours, and var(--token).
paused boolean attribute unset Stops frame scheduling and keeps the current frame visible.
force-motion boolean attribute unset Overrides a reduced-motion preference; use only when appropriate.
aria-label string Loading Accessible label applied to the internal canvas.

Colour modes

Omit both hue and color to preserve the loader's native treatment. That can be a multi-colour palette or deliberately monochrome grey, depending on the movement. Set hue to recolour every visible particle with one hue, or set color for one exact CSS colour. color takes precedence over hue.

States

working, searching, connecting, weaving, shaping, listening, breathing, composing, solving, observing, dreaming, charging, flowing, awakening, coding, transcending, singing, growing, blooming, wandering, decoding, calibrating, attracting, shattering, crystallizing, constellating, folding, echoing, balancing, weathering, mapping, forging, orbiting, racing, bubbling, spinning, electrifying, flocking, throbbing, cascading, spiraling, juggling, eclipsing, resonating, condensing, dispersing, prisming, levitating, synchronizing, unraveling, pondering, deducing, branching, focusing, reflecting, weighing, recalling, tracing, converging, questioning, glimmering, radiating, harmonizing, twinkling, flickering, shimmering, surfacing, vibrating, illuminating, sparkling, loading, buffering, typing, processing, synthesizing, considering, uploading, queuing, associating, evaluating, reasoning, exploring, linking, resolving, and imagining.

Framework adapters

The core package remains a native Web Component. Typed adapters are available for the three frameworks that need framework-specific TypeScript or component integration.

React

import { LoaderszLoader } from "loadersz/react";

export function Status() {
  return (
    <LoaderszLoader
      state="racing"
      size={144}
      speed={1.2}
      ariaLabel="Loading results"
    />
  );
}

Importing loadersz/react also adds type support for direct custom-element JSX:

import "loadersz/react";

const loader = <loadersz-loader state="orbiting" size={96} />;

Vue 3

<script setup lang="ts">
import { LoaderszLoader } from "loadersz/vue";
</script>

<template>
  <LoaderszLoader
    state="bubbling"
    :size="144"
    :speed="1.1"
    aria-label="Loading"
  />
</template>

Svelte 4 and 5

<script lang="ts">
  import 'loadersz/svelte';
</script>

<loadersz-loader state="spiraling" size={144} aria-label="Loading" />

Angular

Use the standalone Angular directive. It makes the native element known to Angular and adds typed bindings without CUSTOM_ELEMENTS_SCHEMA. The runtime import is intentionally explicit, so the same pattern works with a small direct state import. Keep the loader in normal HTML — never inside an <svg> or <defs> block.

import { Component } from "@angular/core";
import "loadersz";
import { LoaderszLoader } from "loadersz/angular";

@Component({
  standalone: true,
  selector: "app-root",
  imports: [LoaderszLoader],
  template: `
    <loadersz-loader
      [state]="state"
      [size]="144"
      aria-label="Loading"
    ></loadersz-loader>
  `,
})
export class App {
  readonly state = "racing" as const;
}

For Solid, Qwik, Lit, and plain HTML, import loadersz and use <loadersz-loader> directly.

Which import should I use?

Framework Loader can change at runtime One fixed loader
React import { LoaderszLoader } from 'loadersz/react' import 'loadersz/racing' plus React's type-only import below
Vue 3 import { LoaderszLoader } from 'loadersz/vue' import 'loadersz/racing' and use the native element
Svelte import 'loadersz/svelte' import 'loadersz/racing' plus import type {} from 'loadersz/svelte'
Angular import 'loadersz' + LoaderszLoader import 'loadersz/racing' + LoaderszLoader
Lit, Solid, Qwik, HTML import 'loadersz' import 'loadersz/racing'

The controls are always the same. The native element spells its multi-word attributes as aria-label, force-motion, and particle-radius; React and Vue wrappers spell them as ariaLabel, forceMotion, and particleRadius. state, size, speed, density, hue, color, theme, and paused keep the same name.

Single-mode imports

When a loader will never change state, use its direct entry point. It includes the Canvas core and exactly one geometry builder-no tree-shaking configuration required.

import "loadersz/racing";
<loadersz-loader
  state="racing"
  size="144"
  speed="1.15"
  aria-label="Loading results"
></loadersz-loader>

The direct entry's visual state is fixed by its import. The state attribute is accepted so markup stays familiar, but cannot load the other 84 animations. Every state name has a matching entry point: loadersz/working, loadersz/solving, loadersz/racing, and so on.

In React, a direct entry can stay small too. Import the fixed runtime entry and the adapter types only; TypeScript removes the type-only import from the browser bundle.

import "loadersz/racing";
import type { LoaderszReactProps } from "loadersz/react";

const loaderProps = { state: "racing", size: 96 } satisfies LoaderszReactProps;

export function Loading() {
  return <loadersz-loader {...loaderProps} aria-label="Loading" />;
}

The direct import itself is framework-agnostic: it always registers the same native element and supports the same state, size, speed, density, hue, color, theme, paused, and force-motion attributes. Framework wrapper types are intentionally specific to their framework: use LoaderszReactProps for React, loadersz/vue for the typed Vue component, and import type {} from 'loadersz/svelte' alongside a direct entry for Svelte template typing. The Vue wrapper imports the complete switchable component, so use the native element when the smallest single-state bundle matters.

For a fixed canvas you own yourself, the same entry point exposes a fixed imperative controller:

import { LoaderszLoader } from "loadersz/racing";

const canvas = document.querySelector<HTMLCanvasElement>("#loading");
if (!canvas) throw new Error("Missing canvas");

const loader = new LoaderszLoader(canvas, {
  size: 144,
  speed: 1.15,
  ariaLabel: "Loading results",
});

Use the root import when a component must change between states at runtime:

import "loadersz";

loadersz/modes remains available for applications that want named fixed-mode factories and already rely on an ESM-aware production bundler:

import { racing } from "loadersz/modes";

const canvas = document.querySelector<HTMLCanvasElement>("#loading");
if (!canvas) throw new Error("Missing canvas");

const loader = racing(canvas, {
  size: 144,
  speed: 1.15,
  ariaLabel: "Loading results",
});

The returned controller still supports setOptions, paused, colour, speed, density, and destroy(). Its visual mode is fixed to the imported factory.

Imperative API

Use the imperative API when your application owns a canvas or controls the lifecycle directly.

import { LoaderszLoader } from "loadersz";

const canvas = document.querySelector<HTMLCanvasElement>("#loader");
if (!canvas) throw new Error("Missing canvas");

const loader = new LoaderszLoader(canvas, {
  state: "solving",
  size: 160,
  speed: 1.1,
  theme: "dark",
  ariaLabel: "Solving",
});

loader.setOptions({ state: "forging", size: 200, hue: 28 });
loader.destroy();

All exported types and methods include TSDoc, so editors show parameter descriptions, defaults, return values, and lifecycle behaviour on hover.

Accessibility and performance

Development

Install dependencies and enable the local Git hooks once:

npm install
npm run hooks:install

Commits follow Conventional Commits. Pre-commit checks format and lint staged files. The release package intentionally contains no install lifecycle scripts.

Browser benchmark UI

Start the interactive production-build benchmark:

npm run benchmark:ui

Open http://127.0.0.1:4174, choose a scenario, and select Run benchmark. Stop the local server with Ctrl+C. See BENCHMARKING.md for A/B comparisons, equivalence checks, CI behaviour, and measurement rules.

License

MIT © 2026 lumberjacque.

loadersz-core (lumberjacque/loadersz-core)

Searching Database…

What is this?loadersz is a dependency-free Canvas loading-animation library for the browser, shipped as a native Web Component with a small imperative TypeScript API and 150 switchable motion states.
Is this a Web Component or a framework-specific library?The core is a plain native Web Component with a TypeScript imperative API - no React, SVG filters, or runtime dependencies required. Typed wrapper adapters are provided on top for React, Vue 3, Svelte, and Angular.
How do I add a loader to my project?Import the package once to register the custom element, then drop into your markup; for apps that only need one state, import a fixed entry like 'loadersz/racing' instead for a smaller bundle.
Do I need React or Vue installed to use it?No - the core has zero regular runtime dependencies. React, Vue, and Svelte are only required as optional peer dependencies if you import their typed adapter entry points.
Can a single-state import switch to a different animation later?No - a direct entry point like loadersz/racing bundles only its own geometry builder, and while the state attribute is still accepted for markup consistency, it can't load any of the other 84 animations.
How many loader animations are included?There are 150 motion states organized into categories like product-style loaders, systems, data and telemetry, reasoning, atmosphere, and motion studies, matching the order on the official website.
Does it respect reduced-motion settings?Yes, it honors prefers-reduced-motion by default and pauses animation in hidden tabs; a force-motion attribute can override this when appropriate.
Can I recolor a loader?Yes - set the hue attribute (0-360) to tint the native palette with one color, or set color for an exact CSS color, which takes precedence over hue.
How small is a single loader bundle?One fixed state weighs about 2.5-3.1 kB gzipped since it only includes the Canvas core plus one geometry builder, with no tree-shaking configuration needed.
Size
0.7 MB
Stars
163
Age
1mo
Commits · 90 Days