How to Create Loading Skeleton in Next.js: Simple Guide
In Next.js, create a loading skeleton by conditionally rendering placeholder components with CSS animations while data loads. Use React state with
useState and useEffect to toggle between the skeleton and actual content.Syntax
To create a loading skeleton in Next.js, you typically use React state to track loading status and conditionally render a skeleton UI or the real content. The skeleton is styled with CSS animations to mimic loading.
useState: to hold loading stateuseEffect: to simulate or detect data loading- Conditional rendering: show skeleton if loading, else show content
- CSS animations: create shimmer effect for skeleton
javascript
import { useState, useEffect } from 'react'; export default function Component() { const [loading, setLoading] = useState(true); useEffect(() => { // Simulate data loading const timer = setTimeout(() => setLoading(false), 3000); return () => clearTimeout(timer); }, []); return ( <div> {loading ? ( <div className="skeleton">Loading...</div> ) : ( <div>Actual content here</div> )} </div> ); }
Output
Shows a gray animated block labeled 'Loading...' for 3 seconds, then displays 'Actual content here'.
Example
This example shows a Next.js functional component that displays a loading skeleton with a shimmer effect for 3 seconds before showing the real content.
javascript
import { useState, useEffect } from 'react'; export default function LoadingSkeletonExample() { const [loading, setLoading] = useState(true); useEffect(() => { const timer = setTimeout(() => setLoading(false), 3000); return () => clearTimeout(timer); }, []); return ( <div style={{ maxWidth: '300px', margin: '20px auto', fontFamily: 'Arial, sans-serif' }}> {loading ? ( <div className="skeleton-box"> <div className="skeleton-avatar" /> <div className="skeleton-line short" /> <div className="skeleton-line" /> <div className="skeleton-line" /> </div> ) : ( <div> <h2>John Doe</h2> <p>This is the loaded content after the skeleton.</p> </div> )} <style jsx>{` .skeleton-box { display: flex; flex-direction: column; gap: 10px; padding: 20px; border: 1px solid #ddd; border-radius: 8px; background: #f0f0f0; } .skeleton-avatar { width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(90deg, #e0e0e0 25%, #f8f8f8 37%, #e0e0e0 63%); background-size: 400% 100%; animation: shimmer 1.5s infinite; margin-bottom: 10px; } .skeleton-line { height: 15px; border-radius: 4px; background: linear-gradient(90deg, #e0e0e0 25%, #f8f8f8 37%, #e0e0e0 63%); background-size: 400% 100%; animation: shimmer 1.5s infinite; } .skeleton-line.short { width: 50%; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } `}</style> </div> ); }
Output
Initially shows a gray avatar circle and three horizontal bars with a shimmering animation for 3 seconds, then displays the text 'John Doe' and a paragraph.
Common Pitfalls
Common mistakes when creating loading skeletons in Next.js include:
- Not using conditional rendering properly, causing skeleton and content to show simultaneously.
- Forgetting to clear timers in
useEffect, which can cause memory leaks. - Using heavy or complex skeleton components that slow down rendering.
- Not adding accessible labels or roles, which hurts screen reader users.
Always ensure skeletons are lightweight and visually distinct from loaded content.
javascript
import { useState, useEffect } from 'react'; // Wrong: No cleanup of timer and no conditional rendering export default function WrongSkeleton() { const [loading, setLoading] = useState(true); useEffect(() => { setTimeout(() => setLoading(false), 3000); }, []); return ( <div> <div className="skeleton">Loading...</div> <div>Actual content here</div> </div> ); } // Right: Conditional rendering and cleanup export function RightSkeleton() { const [loading, setLoading] = useState(true); useEffect(() => { const timer = setTimeout(() => setLoading(false), 3000); return () => clearTimeout(timer); }, []); return ( <div> {loading ? <div className="skeleton">Loading...</div> : <div>Actual content here</div>} </div> ); }
Quick Reference
- useState: Track loading state.
- useEffect: Simulate or detect data loading and cleanup timers.
- Conditional rendering: Show skeleton while loading, real content after.
- CSS animations: Use keyframes for shimmer effect.
- Accessibility: Add ARIA roles or labels if needed.
Key Takeaways
Use React state and conditional rendering to toggle between skeleton and content.
Apply CSS shimmer animations to skeleton elements for a smooth loading effect.
Always clear timers in useEffect to avoid memory leaks.
Keep skeleton UI simple and accessible for better user experience.
Test skeleton appearance on different screen sizes for responsiveness.