# Loader Morphing Shape — React Loader Component

> A single shape turns a quarter at a time while its corners melt between square, circle, leaf and diamond, trailed by a lagging ghost.

- Category: [Loaders](https://pixelpapa.com/components/loaders.md)
- Tags: loader, morph, shape, spinner, loading
- Dependencies: none (plus React and Tailwind CSS v4)
- Last updated: 2026-11-13
- Live preview: https://pixelpapa.com/components/loaders/loader-morphing-shape

## Install

```bash
npx shadcn@latest add "https://pixelpapa.com/r/loader-morphing-shape.json"
```

The file is written to `components/pixelpapa/loader-morphing-shape.tsx`. To install manually, copy the source below into your project. It imports `cn` from `@/lib/utils`, which every shadcn project has.

## Source

```tsx
"use client";

import type { CSSProperties } from "react";
import { cn } from "@/lib/utils";

type LoaderMorphingShapeProps = {
  /** Edge length in px. */
  size?: number;
  /** Seconds for the full four-shape cycle. */
  speed?: number;
  variant?: "solid" | "outline" | "gradient";
  /** Show the delayed echo behind the shape. */
  ghost?: boolean;
  /** Accessible label. */
  label?: string;
  className?: string;
};

export function LoaderMorphingShape({
  size = 44,
  speed = 3.2,
  variant = "solid",
  ghost = true,
  label = "Loading",
  className,
}: LoaderMorphingShapeProps) {
  const paint =
    variant === "outline"
      ? "border-[length:var(--pp-stroke)] border-current"
      : variant === "gradient"
        ? "bg-[conic-gradient(from_210deg,var(--chart-2),var(--primary),var(--chart-3),var(--chart-2))]"
        : "bg-current";
  const animation = `pp-loader-morphing-shape-turn ${speed}s cubic-bezier(0.65, 0, 0.35, 1) infinite`;

  return (
    <span
      role="status"
      aria-label={label}
      className={cn("relative inline-block shrink-0 text-primary", className)}
      style={{ width: size, height: size, "--pp-stroke": `${Math.max(2, size * 0.09)}px` } as CSSProperties}
    >
      <style>{`
        @keyframes pp-loader-morphing-shape-turn {
          0% { border-radius: 24%; transform: rotate(0deg) scale(1); }
          20%, 25% { border-radius: 50%; transform: rotate(90deg) scale(0.82); }
          45%, 50% { border-radius: 50% 8% 50% 8%; transform: rotate(180deg) scale(1); }
          70%, 75% { border-radius: 14%; transform: rotate(315deg) scale(0.78); }
          95%, 100% { border-radius: 24%; transform: rotate(360deg) scale(1); }
        }
        @media (prefers-reduced-motion: reduce) {
          .pp-loader-morphing-shape-el { animation-duration: 12s !important; }
        }
      `}</style>
      {ghost && (
        <span
          className={cn("pp-loader-morphing-shape-el absolute inset-0 rounded-[24%] opacity-25", paint)}
          style={{ animation, animationDelay: `${-speed + speed * 0.07}s` }}
        />
      )}
      <span className={cn("pp-loader-morphing-shape-el absolute inset-0 rounded-[24%]", paint)} style={{ animation }} />
    </span>
  );
}

export default function LoaderMorphingShapeDemo() {
  return (
    <div className="grid w-full max-w-xl grid-cols-3 gap-4">
      {[
        { label: "Solid", note: "44 px", node: <LoaderMorphingShape /> },
        { label: "Outline", note: "56 px", node: <LoaderMorphingShape variant="outline" size={56} speed={3.8} /> },
        { label: "Gradient", note: "72 px", node: <LoaderMorphingShape variant="gradient" size={72} speed={4.4} /> },
      ].map((v) => (
        <div
          key={v.label}
          className="flex flex-col items-center rounded-2xl border border-border bg-card px-4 pt-8 pb-5 shadow-sm"
        >
          <div className="flex h-28 items-center justify-center">{v.node}</div>
          <p className="mt-3 text-sm font-medium text-card-foreground">{v.label}</p>
          <p className="text-xs text-muted-foreground">{v.note}</p>
        </div>
      ))}
      <div className="col-span-3 flex items-center gap-3 rounded-2xl border border-dashed border-border px-5 py-4">
        <LoaderMorphingShape size={20} ghost={false} speed={2.6} />
        <p className="text-sm text-muted-foreground">
          Rendering <span className="font-medium text-foreground">spring-lookbook.mp4</span> · about a minute left
        </p>
      </div>
    </div>
  );
}
```

## AI prompt

```text
Add the "Loader Morphing Shape" component from PixelPapa UI to this project.

What it is: A single shape turns a quarter at a time while its corners melt between square, circle, leaf and diamond, trailed by a lagging ghost.
Category: Loaders. Depends on: no extra packages.

1. Install it with the shadcn CLI:
   npx shadcn@latest add "https://pixelpapa.com/r/loader-morphing-shape.json"
   (If the CLI is unavailable, fetch that URL, write files[0].content to components/pixelpapa/loader-morphing-shape.tsx and install the listed dependencies.)
2. Import it from "@/components/pixelpapa/loader-morphing-shape" and place it where I describe below.
3. Keep its animation timings and accessibility attributes. Replace the demo copy and data with content that fits my product, and use my existing theme tokens (bg-background, text-foreground, bg-primary, border-border) rather than hard-coded colours.

Where to use it: <describe the page and position here>
```

## More loaders

- [Loader Bars Equalizer](https://pixelpapa.com/components/loaders/loader-bars-equalizer.md): Rounded bars bounce at different tempos like an audio meter; grows from the baseline or mirrors around the centre line.
- [Loader Infinity Path](https://pixelpapa.com/components/loaders/loader-infinity-path.md) (Pro): A comet of stroke dashes laps an SVG infinity loop, its tail built from stacked dashes that thin out behind the head.
- [Loader Orbit Dots](https://pixelpapa.com/components/loaders/loader-orbit-dots.md): Dots chase each other around a ring, bunching at the top and fanning out as they fall, around a softly pulsing core.
- [Loader Progress Ring Percent](https://pixelpapa.com/components/loaders/loader-progress-ring-percent.md) (Pro): A ring that springs to its value while the percentage counts along inside it, then swaps to a check mark when it reaches 100.
- [Loader Skeleton Card](https://pixelpapa.com/components/loaders/loader-skeleton-card.md) (Pro): Skeleton blocks for a card and a list, swept by one shared diagonal shimmer per surface so every block glints in sync.
- [Loader Stepped Progress](https://pixelpapa.com/components/loaders/loader-stepped-progress.md) (Pro): A multi-step progress track: connectors fill, the active step spins, finished steps pop a check, and the status line rolls to the next message.
