Part 114: Enhancing User Experience with Skeleton UI in Next.js
[App] Streaming with Suspense

When building web applications, the user experience can be greatly enhanced by providing visual feedback during data loading. One effective method is using a "skeleton" UI as a placeholder while the actual data is being fetched. In this blog post, we'll explore how to implement a skeleton UI using Tailwind CSS in a Next.js application, specifically for a comments section that utilizes React's Suspense and Streaming features.
Understanding the Need for a Skeleton UI
In our previous discussion, we implemented React's Suspense to defer loading a comments section until after the rest of the page content has been displayed. While this approach speeds up the initial load, the default "Loading..." text fallback may not provide the best user experience. A skeleton UI can improve this by offering a visual outline of the content structure, giving users a clearer idea of what's coming.
Creating a Skeleton Component
We'll create a CommentListSkeleton component to serve as a placeholder for the comments section. This component will mimic the structure of the actual comments list but without any real data.
Step-by-Step Implementation
Copy the Structure: Start by copying the existing
CommentListcomponent and rename it toCommentListSkeleton.Remove Data Fetching: The skeleton component should render immediately without fetching data. Remove any async operations and props related to real data.
Use Placeholder Elements: Replace the actual data elements with placeholders. We'll use simple
divelements styled with Tailwind CSS to represent the user icon and message text.Add Styling: Use Tailwind CSS to style the placeholders. We'll add a gray background, rounded corners, and a pulse animation to simulate loading.
Here's the code for the CommentListSkeleton component:
// components/CommentListSkeleton.jsx
import { UserCircleIcon } from '@heroicons/react/24/outline';
export default function CommentListSkeleton() {
return (
<ul className="animate-pulse border mt-3 rounded">
{[1, 2, 3].map((index) => (
<li key={index}
className="border-b px-3 py-2 last:border-none odd:bg-orange-100">
<div className="flex gap-3 items-center pb-1 text-slate-300">
<UserCircleIcon className="h-6 w-6" />
<div className="bg-gray-300 rounded h-3 w-24" />
</div>
<p className="py-1">
<div className="bg-gray-300 rounded h-3 w-2/3" />
</p>
</li>
))}
</ul>
);
}Integrating the Skeleton with Suspense
To use the CommentListSkeleton, integrate it as the fallback prop in the Suspense component wrapping the CommentList.
// app/reviews/[slug]/page.jsx
import { Suspense } from 'react';
import CommentForm from '@/components/CommentForm';
import CommentList from '@/components/CommentList';
import CommentListSkeleton from '@/components/CommentListSkeleton';
export default async function ReviewPage({ params: { slug } }) {
// ...other code
return (
<>
{/* ...other content */}
<section>
<h2>Comments</h2>
<CommentForm slug={slug} title={review.title} />
<CommentList slug={slug} />
</Suspense>
</section>
</>
);
}Benefits of Skeleton UI
Improved User Experience: Skeleton UI provides a better loading experience by visually representing the content structure.
Increased Engagement: Users are more likely to remain engaged with a page that offers a clear indication of incoming content.
Visual Feedback: Offers users an immediate visual cue that the content is loading, reducing perceived wait time.
Conclusion
Skeleton UIs are a powerful tool for enhancing user experience during data loading. By providing a visual structure of the page, they help set user expectations and reduce frustration. While this approach is not specific to Next.js, integrating it with Suspense allows for a smooth and responsive experience, making it a valuable addition to any modern web application.
Remember, the goal is to create a seamless experience for users, and a well-implemented skeleton UI can significantly contribute to achieving that.
Last updated