This implementation provides a comprehensive skeleton loading system for React applications using Tailwind CSS. It includes reusable skeleton components that match the application's actual component layouts, ensuring smooth loading transitions without layout shift.
✅ Placeholder cards appear during loading ✅ Loading state transitions smoothly ✅ No content layout shift ✅ Dark mode support ✅ Customizable animations ✅ TypeScript support ✅ Responsive design
Skeleton- Base skeleton componentSkeletonText- Multi-line text skeletonSkeletonAvatar- Avatar/profile picture skeletonSkeletonButton- Button skeletonSkeletonImage- Image/thumbnail skeletonSkeletonBadge- Badge/chip skeletonSkeletonCard- Card wrapper skeleton
-
CourseCardSkeleton (
src/components/skeleton/CourseCardSkeleton.tsx)- Matches CourseCard component layout exactly
- Includes image, title, description, stats, and button placeholders
CourseGridSkeletonfor multiple cards
-
DashboardSkeleton (
src/components/skeleton/DashboardSkeleton.tsx)DashboardStatsCardSkeleton- Stats cardsDashboardProgressCardSkeleton- Progress cardsDashboardActivityCardSkeleton- Activity/events cardsDashboardRecommendationsCardSkeleton- Recommendation cardsDashboardCommunityCardSkeleton- Community activity cardsDashboardSkeleton- Full dashboard layout
-
FeatureCardSkeleton (
src/components/skeleton/FeatureCardSkeleton.tsx)- Individual feature card skeleton
FeatureCardsGridSkeleton- Full feature section
-
CodePlaygroundSkeleton (
src/components/skeleton/CodePlaygroundSkeleton.tsx)CodeEditorSkeleton- Code editor areaOutputPanelSkeleton- Output panelSavedSnippetsSkeleton- Saved snippets listSaveCodeFormSkeleton- Save formDocumentationPanelSkeleton- Documentation panelCodePlaygroundSkeleton- Full playground layout
-
TestimonialSkeleton (
src/components/skeleton/TestimonialSkeleton.tsx)- Individual testimonial card skeleton
TestimonialsSkeleton- Full testimonials section
-
HeroSkeleton (
src/components/skeleton/HeroSkeleton.tsx)HeroSkeleton- Full hero sectionHeroSectionSkeleton- Simplified hero section
Custom hook for managing loading states with smooth transitions:
const { isLoading, startLoading, stopLoading, setLoading } = useLoading({
minLoadingTime: 800, // Minimum loading time in ms
initialLoading: false,
});Hook for data fetching with automatic loading states:
const { data, error, isLoading } = useAsyncData(
() => fetchCourses(),
[dependencies],
{ minLoadingTime: 1200 },
);// src/app/courses/loading.tsx
import { CourseGridSkeleton } from '@/components/skeleton';
export default function CoursesLoading() {
return (
<main className="container mx-auto px-4 py-8">
<CourseGridSkeleton count={6} />
</main>
);
}import { CourseGridSkeleton } from '@/components/skeleton';
import { useLoading } from '@/hooks/useLoading';
export function FeaturedCourses() {
const { isLoading } = useLoading();
if (isLoading) {
return <CourseGridSkeleton count={3} />;
}
return (
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{courses.map((course, index) => (
<div
key={course.id}
className="animate-fade-in"
style={{ animationDelay: `${index * 100}ms` }}
>
<CourseCard {...course} />
</div>
))}
</div>
);
}import { DashboardSkeleton } from '@/components/skeleton';
export function Dashboard() {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchDashboardData().then(() => setIsLoading(false));
}, []);
return (
<div>
{isLoading ? <DashboardSkeleton /> : <DashboardContent />}
</div>
);
}module.exports = {
theme: {
extend: {
animation: {
skeleton: 'skeleton 2s ease-in-out infinite alternate',
'pulse-skeleton': 'pulse-skeleton 1.5s ease-in-out infinite',
},
keyframes: {
skeleton: {
'0%': { backgroundColor: 'hsl(210, 40%, 94%)' },
'100%': { backgroundColor: 'hsl(210, 40%, 98%)' },
},
'pulse-skeleton': {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0.5' },
},
},
},
},
};@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fadeIn 0.6s ease-in-out forwards;
}