Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Fallback UI i Skeleton Loaders

W kosmosie, gdy moduł stacji jest w trakcie inicjalizacji, zaloga widzi wskazniki postepu i holograficzne kontury przyszlego interfejsu. W React mamy skeleton loadery i fallback UI - elementy zastepczego interfejsu, które informują użytkownika o trwającym ladowaniu.

Dlaczego Fallback UI jest ważny?

Dobre ładowanie to nie tylko spinner. Uzytkownik powinien miec poczucie, że cos sie dzieje i wiedziec, czego sie spodziewac.

Poziomy jakosci ładowania (od najgorszego do najlepszego)

1// 1. Nic (najgorsze) - użytkownik nie wie co sie dzieje
2<Suspense fallback={null}>
3
4// 2. Prosty tekst
5<Suspense fallback={<p>Ladowanie...</p>}>
6
7// 3. Spinner/Loader
8<Suspense fallback={<Spinner />}>
9
10// 4. Skeleton loader (najlepsze)
11<Suspense fallback={<ContentSkeleton />}>

Skeleton Loaders

Skeleton loader to zastepczy UI, który naśladuje kształt i układ docelowej treści. Uzytkownik widzi "szkielet" strony, który stopniowo wypelnia sie prawdziwą treścią.

Podstawowy Skeleton w CSS

1function CardSkeleton() {
2  return (
3    <div className="skeleton-card">
4      <div className="skeleton-image skeleton-pulse" />
5      <div className="skeleton-title skeleton-pulse" />
6      <div className="skeleton-text skeleton-pulse" />
7      <div className="skeleton-text skeleton-pulse short" />
8    </div>
9  );
10}
1.skeleton-pulse {
2  background: linear-gradient(
3    90deg,
4    #e0e0e0 25%,
5    #f0f0f0 50%,
6    #e0e0e0 75%
7  );
8  background-size: 200% 100%;
9  animation: pulse 1.5s infinite;
10  border-radius: 4px;
11}
12
13@keyframes pulse {
14  0% { background-position: 200% 0; }
15  100% { background-position: -200% 0; }
16}
17
18.skeleton-image {
19  width: 100%;
20  height: 200px;
21  margin-bottom: 12px;
22}
23
24.skeleton-title {
25  width: 60%;
26  height: 24px;
27  margin-bottom: 8px;
28}
29
30.skeleton-text {
31  width: 100%;
32  height: 16px;
33  margin-bottom: 6px;
34}
35
36.skeleton-text.short {
37  width: 40%;
38}

Skeleton dla różnych typow treści

Lista elementów

1function ListSkeleton({ count = 3 }) {
2  return (
3    <div className="list-skeleton">
4      {Array.from({ length: count }).map((_, i) => (
5        <div key={i} className="list-item-skeleton">
6          <div className="avatar-skeleton skeleton-pulse" />
7          <div className="info-skeleton">
8            <div className="skeleton-pulse name-skeleton" />
9            <div className="skeleton-pulse detail-skeleton" />
10          </div>
11        </div>
12      ))}
13    </div>
14  );
15}

Dashboard z wieloma sekcjami

1function DashboardSkeleton() {
2  return (
3    <div className="dashboard-skeleton">
4      <div className="stats-row">
5        <StatCardSkeleton />
6        <StatCardSkeleton />
7        <StatCardSkeleton />
8      </div>
9      <div className="main-content">
10        <ChartSkeleton />
11        <ListSkeleton count={5} />
12      </div>
13    </div>
14  );
15}

Wzorce ładowania

Progressive Loading

Laduj ważne treści najpierw, reszta ładuje sie w tle:

1function MissionDashboard() {
2  return (
3    <div>
4      {/* Krytyczna tresc - bez lazy loading */}
5      <MissionStatus />
6
7      {/* Mniej krytyczna - lazy loading z skeleton */}
8      <Suspense fallback={<ChartSkeleton />}>
9        <MissionChart />
10      </Suspense>
11
12      {/* Najmniej ważna - ładuje sie ostatnia */}
13      <Suspense fallback={<ListSkeleton count={5} />}>
14        <MissionHistory />
15      </Suspense>
16    </div>
17  );
18}

Inline Loading States

Nie zawsze potrzebujesz Suspense. Dla danych ladowanych asynchronicznie uzywaj stanow ładowania:

1function CrewMember({ id }) {
2  const [member, setMember] = useState(null);
3  const [loading, setLoading] = useState(true);
4
5  useEffect(() => {
6    fetchCrewMember(id)
7      .then(setMember)
8      .finally(() => setLoading(false));
9  }, [id]);
10
11  if (loading) return <MemberSkeleton />;
12
13  return (
14    <div className="crew-member">
15      <img src={member.avatar} alt={member.name} />
16      <h3>{member.name}</h3>
17      <p>{member.role}</p>
18    </div>
19  );
20}

Dobre praktyki

  1. Skeleton powinien odwzorowywac układ treści - taki sam rozmiar i ksztalt
  2. Animacja pulsu - daje poczucie że cos sie dzieje
  3. Unikaj migotania - jeśli ładowanie trwa <300ms, nie pokazuj skeletonow
  4. Uzyj różnych skeletonow dla różnych typow treści
  5. Zachowaj proporcje - skeleton powinien zajmowac tyle samo miejsca co prawdziwa treść
Przejdź do CodeWorlds