Propel

Logo Spinner

The animated Plane mark, shown while a whole screen is waiting on app boot or a page transition.

Show code
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function BasicDemo() {
  return <LogoSpinner size="md" alt="Loading Plane" />;
}

Installation

import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

Usage

The animated Plane mark, for the moments a whole screen is waiting — app boot, a splash screen, a full-page route transition. For pending state inside a control or a section, use CircularProgress (or a control’s own loading state) instead: this is brand furniture, not a status glyph.

Both themes’ artwork is rendered and CSS shows the one matching the active data-theme, so a theme flip costs no JS and no second request (both files load with the page — 88KB together). Under prefers-reduced-motion: reduce the same CSS paints a still of the fully drawn mark instead of the animation, since a GIF cannot be paused. alt is required — name what the app is waiting on; only the visible image is in the accessibility tree, so it is announced once.

import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function BasicDemo() {
  return <LogoSpinner size="md" alt="Loading Plane" />;
}

Examples

Sizes

Mark height: sm 24px inline, md 44px full page. The width follows the artwork’s aspect ratio.

Show code
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function SizesDemo() {
  return (
    <div className="flex items-center gap-6">
      <LogoSpinner size="sm" alt="sm mark" />
      <LogoSpinner size="md" alt="md mark" />
    </div>
  );
}

Responsive

size="fluid" is the third step and the one a splash screen wants: 24px on a phone, 44px from the 640px breakpoint up. It is the same recipe the apps’ own splash CSS used. Above that breakpoint it is by definition indistinguishable from md — narrow the window past 640px to see it step down.

Show code
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function ResponsiveDemo() {
  return <LogoSpinner size="fluid" alt="Loading Plane" />;
}

Assets

This is the only propel component that ships binary assets: the mark is designed frame-by-frame artwork (65 frames per theme), not something tokens and CSS can express. The GIFs ship inside the package next to the component, each with a PNG still of the fully drawn mark for reduced motion, and all four are pulled in as static asset imports, so your bundler turns them into URLs at build time — Vite, webpack 5+, Parcel, or Rollup with an asset plugin.

If you render on the server (SSR, or a prerendered SPA shell), tell your bundler not to externalize propel, or the asset import reaches Node — which cannot load a .gif — and the render fails:

// vite.config.ts
export default defineConfig({
  ssr: { noExternal: ["@makeplane/propel"] },
});

The files are also exported raw (@makeplane/propel/components/logo-spinner/logo-spinner-light.gif, …/logo-spinner-light-still.png), so an app’s own pre-hydration splash CSS can point at the same artwork instead of keeping a copy.

They are exported at 158×88 / 159×88, twice the largest size they are ever displayed at (44px tall), so they stay crisp on 2x screens while weighing 39KB and 48KB; the stills are under 1KB each.

API Reference

LogoSpinner

The animated Plane mark, for the moments a whole screen is waiting — app boot, a splash screen, a full-page route transition. For pending state inside a control or a section, use CircularProgress (or a control's own loading state) instead: this is brand furniture, not a status glyph. Both themes' artwork is rendered and CSS shows the one matching the active data-theme, so a theme flip costs no JS and no second request. Under prefers-reduced-motion: reduce the same CSS paints a still of the fully drawn mark instead of the animation. Only the visible image is in the accessibility tree (the other three are display: none), so alt is announced once.

PropTypeDefaultDescription
size(required)"sm" | "md" | "fluid"—Mark height: sm 24px, md 44px, fluid 24px stepping to 44px at the 640px breakpoint.
alt(required)string—Alternative text (required) — what the app is waiting on, e.g. "Loading Plane".
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.