Kurumsal Dashboard'da Skeleton Loading ve UX Optimizasyonu
React'te skeleton loading nasıl uygulanır? CSS-only skeleton, React Suspense, optimistic updates ve error boundary ile dashboard UX optimizasyonu rehberi.
Neden Skeleton Loading?
Veri yüklenirken boş ekran veya dönen spinner göstermek hızlı hissetmez. Skeleton loading, içeriğin yerleşimini göstererek kullanıcıya “veri geliyor” mesajı verir — deneyim %40 daha hızlı algılanır.
CSS-Only Skeleton (Framework Bağımsız)
.skeleton {
background: linear-gradient(
90deg,
var(--color-bg-muted) 25%,
var(--color-border) 50%,
var(--color-bg-muted) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton-text {
height: 1em;
margin-bottom: 0.5em;
}
.skeleton-card {
height: 120px;
border-radius: 12px;
}
.skeleton-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
<!-- Yükleniyor durumu -->
<div class="kpi-card">
<div class="skeleton skeleton-text" style="width: 60%"></div>
<div class="skeleton skeleton-text" style="width: 40%"></div>
</div>
React Skeleton Bileşeni
interface SkeletonProps {
width?: string;
height?: string;
borderRadius?: string;
count?: number;
}
export function Skeleton({
width = '100%',
height = '1em',
borderRadius = '4px',
count = 1
}: SkeletonProps) {
return (
<>
{Array.from({ length: count }).map((_, i) => (
<div
key={i}
className="skeleton"
style={{ width, height, borderRadius }}
/>
))}
</>
);
}
// KPI kartı skeleton'ı
export function KPICardSkeleton() {
return (
<div className="kpi-card">
<Skeleton width="40%" height="0.875em" />
<Skeleton width="60%" height="2em" />
<Skeleton width="30%" height="0.75em" />
</div>
);
}
// Tablo skeleton'ı
export function TableSkeleton({ rows = 5 }: { rows?: number }) {
return (
<div className="table-skeleton">
<Skeleton height="2.5em" borderRadius="8px" />
{Array.from({ length: rows }).map((_, i) => (
<Skeleton key={i} height="3em" />
))}
</div>
);
}
Skeleton vs Spinner — Ne Zaman Hangisi?
| Durum | Skeleton | Spinner |
|---|---|---|
| İlk sayfa yüklemesi | ✅ | ❌ |
| Tablo veri yükleme | ✅ | ❌ |
| Buton tıklama (kaydet) | ❌ | ✅ |
| Modal açılışı | ❌ | ✅ |
| Infinite scroll | ✅ | ✅ |
| Dosya yükleme | ❌ | ✅ (progress bar) |
Kural: İçeriğin yerleşimi biliniyorsa → Skeleton. Bilinmiyorsa → Spinner.
React Suspense ile Entegrasyon
import { Suspense, lazy } from 'react';
const DashboardStats = lazy(() => import('./DashboardStats'));
function Dashboard() {
return (
<div className="dashboard">
<Suspense fallback={<KPICardSkeleton />}>
<DashboardStats />
</Suspense>
<Suspense fallback={<TableSkeleton rows={10} />}>
<RecentOrders />
</Suspense>
</div>
);
}
Optimistic Updates
Kullanıcı aksiyonunu anında UI’da gösterip, arka planda API’ye gönderin:
function TodoList() {
const [todos, setTodos] = useState<Todo[]>([]);
const addTodo = async (text: string) => {
const optimisticTodo = { id: crypto.randomUUID(), text, done: false };
// 1. Anında UI'da göster
setTodos(prev => [...prev, optimisticTodo]);
try {
// 2. Arka planda API'ye gönder
const saved = await api.createTodo(text);
// 3. Gerçek ID ile güncelle
setTodos(prev => prev.map(t =>
t.id === optimisticTodo.id ? saved : t
));
} catch {
// 4. Hata olursa geri al
setTodos(prev => prev.filter(t => t.id !== optimisticTodo.id));
}
};
}
Error Boundary
Hata durumunda tüm sayfayı çökertmek yerine ilgili bölümü gösterin:
import { Component, ReactNode } from 'react';
interface Props { children: ReactNode; fallback: ReactNode; }
interface State { hasError: boolean; }
class ErrorBoundary extends Component<Props, State> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return this.props.fallback;
return this.props.children;
}
}
// Kullanım
<ErrorBoundary fallback={<p>Bu bölüm yüklenemedi.</p>}>
<DashboardStats />
</ErrorBoundary>
İlgili Yazılar
- Dark Mode Implementasyonu — Skeleton renkleri tema ile
- Responsive Tablo Tasarımı — Tablo skeleton’ı
- React Query Cache Stratejisi — Cache ile loading azaltma
Bu rehber React’in Suspense ve Error Boundary dokümantasyonuna dayanarak hazırlanmıştır.