Zum Inhalt springen
UI Kit
Accent

Skeleton Loader für React

Platzhalter in der Form des Inhalts, mit weichem Licht, das bei reduzierter Bewegung stillsteht.

Eine von 4 Components in Fortschritt und Zustände

3 Variants

Platzhalter in der Form des Inhalts

Ein Skeleton zeigt die Form dessen, was gleich kommt, während Daten laden. So springt die Seite nicht, wenn der Inhalt ankommt, und Menschen wissen schon, wohin sie schauen. Diese Skeletons übernehmen das Layout der Kit-Components, beim Wechsel vom Platzhalter zum Inhalt bleibt jede Zeile an ihrem Platz.

„Karten“ passt zu Grids aus Produkten, Beiträgen oder Personen. „Liste und Dashboard“ deckt Zeilen mit Avataren und die Blöcke eines App-Screens ab, Zahlen, Diagramme und Tabellen. „Artikel“ bildet einen Beitrag mit Titel, Metazeile, Bild und Absätzen nach. Ein sanftes Licht gleitet über die Formen, bei reduzierter Bewegung steht es still.

Zeig ein Skeleton erst bei Ladezeiten über etwa 300 Millisekunden, kürzere flackern. Gib so viele Platzhalterzeilen vor, wie meist ankommen. Markiere den Bereich für Screenreader als beschäftigt, die Skeletons setzen aria-busy und nehmen ihre Formen aus der Lesereihenfolge.

Variants

Karten

Artikelkarten, die ohne Sprung zum Inhalt wechseln.

Liste und Dashboard

Eine Posteingangsliste und ein Bericht mit Zahlen und Diagramm.

Artikel

Ein Langtext im fertigen Rhythmus, während er lädt.

Details

Was Platzhalter mitbringt, aus Registry und Quellcode gelesen.

Bereich
Elements
Variants
3
Version
1.0.0 · seit 1.0.0
Datei
components/ui/skeleton.tsx
Bringt mit
utilskit
npm-Pakete
Keine
Sprachen
Englisch und Deutsch, 1 eigener Text
Studio
Im Code anpassen

Props

Die Schnittstelle SkeletonProps aus components/ui/skeleton.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch.

  • shapeoptional
    'line' | 'circle' | 'rect'

    line: text height, rounded. circle: avatars. rect: images and cards.

  • styleoptional
    React.CSSProperties

Eingebaute Texte

Diese Texte schreibt Platzhalter selbst. Die Sprache folgt dem KitProvider, über die Prop labels ersetzt du einzelne.

Alle 1 Texte anzeigen
loadingEnglisch LoadingDeutsch Wird geladen

Barrierefreiheit

Aus dem Quellcode der Component abgeleitet.

  • Setzt aria-busy und aria-live, damit Screenreader Zweck und Zustand ansagen.
  • Meldet Änderungen über eine Live-Region, ohne den Fokus zu verschieben.
  • Ergänzt Text für Screenreader, wo Icons und Grafiken allein stehen.
  • Der Server rendert den Startzustand vollständig, JavaScript ergänzt nur die Interaktion.

Bewegung

  • Bringt eigene Animationen mit, ruhig getaktet und ohne Wackeln.
  • Bei reduzierter Bewegung (prefers-reduced-motion) bleibt sie ruhig.

Quellcode

Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.

Datei anzeigen, 112 Zeilen
components/ui/skeleton.tsx
'use client';

import { useLayoutEffect, useRef, useState } from 'react';
import { cn, prefersReducedMotion, useTexts } from '@/lib/kit';

const TEXTS = {
  en: { loading: 'Loading' },
  de: { loading: 'Wird geladen' },
};

/*
 * A slow, slanted sheen that sweeps across and then rests, instead of a pulse.
 * All placeholders start together, so the light reads as one pass over the page.
 * When content arrives, the placeholders fade out over it and the content
 * comes in from a soft blur, one item after another.
 */
const STYLES =
  '.kitfx-sk::after{content:"";position:absolute;inset:0 -50%;transform:translateX(-60%) skewX(-18deg);background:linear-gradient(90deg,transparent 30%,color-mix(in oklab,var(--foreground) 5%,transparent) 44%,color-mix(in oklab,color-mix(in oklab,var(--primary) 30%,var(--foreground)) 9%,transparent) 50%,color-mix(in oklab,var(--foreground) 5%,transparent) 56%,transparent 70%);animation:kitfx-sk 2.4s cubic-bezier(.45,.05,.35,1) infinite}' +
  '@keyframes kitfx-sk{0%{transform:translateX(-60%) skewX(-18deg)}62%,100%{transform:translateX(60%) skewX(-18deg)}}' +
  '.kitfx-sk-ghost{animation:kitfx-sk-out .45s var(--ease-kit) forwards}@keyframes kitfx-sk-out{to{opacity:0;filter:blur(2px)}}' +
  '.kitfx-sk-reveal>:not(.kitfx-sk-ghost){animation:kitfx-sk-in .65s var(--ease-kit) both}' +
  '.kitfx-sk-reveal>:nth-child(2){animation-delay:70ms}.kitfx-sk-reveal>:nth-child(3){animation-delay:140ms}.kitfx-sk-reveal>:nth-child(4){animation-delay:210ms}.kitfx-sk-reveal>:nth-child(n+5){animation-delay:280ms}' +
  '@keyframes kitfx-sk-in{from{opacity:0;filter:blur(6px);transform:translateY(6px)}}' +
  '@media (prefers-reduced-motion:reduce){.kitfx-sk::after{animation:none;opacity:0}.kitfx-sk-ghost{display:none}.kitfx-sk-reveal>*{animation:none}}';

export interface SkeletonProps {
  /** line: text height, rounded. circle: avatars. rect: images and cards. */
  shape?: 'line' | 'circle' | 'rect';
  className?: string;
  style?: React.CSSProperties;
}

/**
 * A placeholder that holds the space of content still loading. A slanted light
 * sweeps over it and rests; with reduced motion it stays still. Decorative: wrap
 * a group in <SkeletonGroup> so screen readers hear "Loading" once.
 */
export function Skeleton({ shape = 'line', className, style }: SkeletonProps) {
  return (
    <>
      {/* React 19 hoists this once into the head, however many skeletons render */}
      <style href="kitfx-sk" precedence="default">
        {STYLES}
      </style>
      <span
        aria-hidden
        style={style}
        className={cn(
          'kitfx-sk relative block overflow-hidden bg-[color-mix(in_oklab,var(--foreground)_7%,transparent)]',
          shape === 'line' && 'h-3 rounded-full',
          shape === 'circle' && 'aspect-square rounded-full',
          shape === 'rect' && 'rounded-lg',
          className,
        )}
      />
    </>
  );
}

/** Several lines of text, the last one shorter, like a paragraph. */
export function SkeletonText({ lines = 3, className }: { lines?: number; className?: string }) {
  const widths = ['100%', '94%', '97%', '88%', '92%'];
  return (
    <span className={cn('block space-y-2.5', className)} aria-hidden>
      {Array.from({ length: lines }, (_, i) => (
        <Skeleton key={i} style={{ width: i === lines - 1 && lines > 1 ? '62%' : widths[i % widths.length] }} />
      ))}
    </span>
  );
}

/**
 * Wraps placeholders: marks the region busy and announces "Loading" once.
 * When `loading` turns false the children are replaced by `content`: the
 * placeholders fade out on top while the content comes in, so nothing jumps.
 */
export function SkeletonGroup({ loading = true, label, content, className, children }: { loading?: boolean; label?: string; content?: React.ReactNode; className?: string; children: React.ReactNode }) {
  const t = useTexts(TEXTS);
  const was = useRef(loading);
  const [swap, setSwap] = useState(false);
  // Before paint, so the content never shows a frame without its entrance
  useLayoutEffect(() => {
    const before = was.current;
    was.current = loading;
    if (!before || loading || prefersReducedMotion()) {
      setSwap(false);
      return;
    }
    setSwap(true);
    const timer = window.setTimeout(() => setSwap(false), 700);
    return () => window.clearTimeout(timer);
  }, [loading]);
  return (
    <div aria-busy={loading} aria-live="polite" className={cn(swap && 'kitfx-sk-reveal relative', className)}>
      {loading ? (
        <>
          <span className="sr-only">{label ?? t.loading}</span>
          {children}
        </>
      ) : (
        <>
          {content}
          {swap && (
            <div aria-hidden className={cn('kitfx-sk-ghost pointer-events-none absolute inset-0 z-10', className)} style={{ margin: 0 }}>
              {children}
            </div>
          )}
        </>
      )}
    </div>
  );
}

Häufige Fragen

Wie installiere ich Platzhalter?

Mit aiw add @ai-website/skeleton. Der Befehl legt die Datei components/ui/skeleton.tsx in dein Projekt, der Code gehört danach dir. Weitere npm-Pakete braucht sie nicht.

Welche Varianten hat Platzhalter?

3 Varianten, Karten, Liste und Dashboard und Artikel.

Kann ich die Texte von Platzhalter übersetzen?

Ja. Platzhalter bringt 1 eigene Texte auf Deutsch und Englisch mit, etwa „Wird geladen“. Die Sprache folgt dem KitProvider, einzelne Texte ersetzt du über die Prop labels.

Kann ein KI-Assistent Platzhalter einbauen?

Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Platzhalter mit aiw_components und installieren sie mit aiw_add @ai-website/skeleton.

Oft gesucht

Direkt zu

In die Zwischenablage kopiert