Skip to content

Conversation

@manthansubhash01
Copy link

Related Tickets & Documents

Fixes: #3293

Description

Added skeleton loaders across the blog website to improve user experience during content loading. This provides visual feedback to users while data is being fetched, making the application feel more responsive and reducing perceived loading times.

The implementation uses shadcn/ui's skeleton component as the base and creates custom skeleton loaders for different content types (hero posts, post cards, post grids, and top blogs).

Changes

  • Added shadcn/ui skeleton component to the project
  • Created 4 custom skeleton components (HeroPostSkeleton, PostCardSkeleton, PostGridSkeleton, TopBlogsSkeleton)
  • Integrated loading states on homepage, community page, and technology page
  • Added proper loading state management with conditional rendering
  • Updated component structure to support skeleton loaders

Type of Change

  • New feature (change that adds functionality)
  • UI improvement (visual or design changes)

Testing

  • Tested skeleton loaders appear correctly during initial page load
  • Verified smooth transition from skeleton to actual content
  • Tested responsive behavior across mobile, tablet, and desktop viewports
  • Confirmed no console errors or warnings
  • Verified all pages (home, community, technology) display appropriate skeletons
  • Tested with slow network throttling to ensure skeletons remain visible during loading

Demo

Before

blank_page2 blank_page1 Screenshot 2025-12-19 at 2 59 31 PM

After

skeleton_loader4 skeleton_loader1 skeleton_loader2 skeleton_loader5 skeleton_loader3

Environment and Dependencies

  • New Dependencies:

    • @radix-ui/react-slot (added by shadcn/ui)
    • class-variance-authority (for component variants)
    • tailwind-merge (for className utilities)
    • lucide-react (for icons, if not already present)
  • Configuration Changes:

    • Added components.json for shadcn/ui configuration
    • Added lib/utils.ts for cn() utility function
    • No environment variable changes required

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added corresponding tests
  • I have run the build command to ensure there are no build errors
  • My changes have been tested across relevant browsers/devices
  • For UI changes, I've included visual evidence of my changes

Copilot AI review requested due to automatic review settings December 19, 2025 09:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds skeleton loaders across the blog website to improve perceived loading times and provide visual feedback during content loading. The implementation uses shadcn/ui's skeleton component as a foundation and creates custom skeleton components for different content types.

Key changes:

  • Added base skeleton component and 4 custom skeleton variations (HeroPostSkeleton, PostCardSkeleton, PostGridSkeleton, TopBlogsSkeleton)
  • Implemented client-side loading states with artificial 500ms delays on homepage, community, and technology pages
  • Removed the global PageLoader component and route change loading indicators from _app.tsx

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
lib/utils.ts Adds cn() utility function for merging Tailwind classes
components/ui/skeleton.tsx Base skeleton component with pulse animation
components/skeletons/HeroPostSkeleton.tsx Skeleton loader for hero post layout
components/skeletons/PostCardSkeleton.tsx Skeleton loader for individual post cards
components/skeletons/PostGridSkeleton.tsx Skeleton loader for post grid layouts
components/skeletons/TopBlogsSkeleton.tsx Skeleton loader for top blogs list (unused)
pages/index.tsx Adds loading state with artificial delay for homepage posts
pages/community/index.tsx Adds loading state with artificial delay for community posts
pages/technology/index.tsx Adds loading state with artificial delay for technology posts
components/topBlogs.tsx Integrates skeleton loaders for technology and community blog sections
components/more-stories.tsx Adds skeleton loader for initial load state
pages/_app.tsx Removes PageLoader component and route transition loading logic
package-lock.json Updates package name from "blog-website" to "keploy-blog-website"
Comments suppressed due to low confidence (1)

pages/technology/index.tsx:42

  • During the loading state, the Layout component receives undefined values for featuredImage and Title props (heroPost is undefined when displayPosts is empty). This could cause issues with SEO meta tags and OpenGraph data. Consider using the original edges data for Layout props or provide fallback values.
    <Layout
      preview={preview}
      featuredImage={heroPost?.featuredImage?.node.sourceUrl}
      Title={heroPost?.title}
      Description={`Blog from the Technology Page`}
    >

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

pages/index.tsx Outdated
Comment on lines 15 to 43
const [isLoading, setIsLoading] = useState(true);
const [displayPosts, setDisplayPosts] = useState({
community: [],
technology: [],
});

useEffect(() => {
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts({
community: communityPosts || [],
technology: technologyPosts || [],
});
setIsLoading(false);
}, 500);

return () => clearTimeout(timer);
}
}, [communityPosts, technologyPosts]);

return (
<Layout
preview={preview}
featuredImage={HOME_OG_IMAGE_URL}
Title={`Blog - Keploy`}
Description={"The Keploy Blog offers in-depth articles and expert insights on software testing, automation, and quality assurance, empowering developers to enhance their testing strategies and deliver robust applications."}>
Description={
"The Keploy Blog offers in-depth articles and expert insights on software testing, automation, and quality assurance, empowering developers to enhance their testing strategies and deliver robust applications."
}
>
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loading state starts as true, which means the statically generated HTML (from getStaticProps) will contain skeleton loaders instead of actual content. This defeats the purpose of static generation and harms SEO, as search engines and users will initially see skeleton loaders. For statically generated pages, the content should be rendered immediately without a loading state.

Copilot uses AI. Check for mistakes.
}
}, [edges]);

console.log("tech posts: ", edges.length);
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.log statements should be removed from production code. This debug statement can cause performance issues and expose implementation details in production.

Suggested change
console.log("tech posts: ", edges.length);

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 40
function MyApp({ Component, pageProps }: AppProps) {
const [loading, setLoading] = useState(false);
useEffect(() => {
// Used for page transition
const startLoader = () => {
setLoading(true);
};
const stopLoader = () => {
setLoading(false);
};
Router.events.on("routeChangeStart", startLoader);
Router.events.on("routeChangeComplete", stopLoader);
Router.events.on("routeChangeError", stopLoader);
return () => {
Router.events.off("routeChangeStart", startLoader);
Router.events.off("routeChangeComplete", stopLoader);
Router.events.off("routeChangeError", stopLoader);
};
}, []);

return (
<>
<AnimatePresence>
{loading ? <PageLoader /> : <Component {...pageProps} />}
<Component {...pageProps} />
</AnimatePresence>
</>
);
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of the PageLoader component and route change listeners means users will no longer see loading feedback during page navigation. This could negatively impact user experience, especially on slower connections. Consider keeping some form of loading indicator for route transitions, or document why this was intentionally removed.

Copilot uses AI. Check for mistakes.
Comment on lines 45 to 52
<Layout
preview={preview}
featuredImage={heroPost?.featuredImage?.node.sourceUrl}
Title={heroPost?.title}
Description={`Blog from the Technology Page`}
>
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During the loading state, the Layout component receives undefined values for featuredImage and Title props (heroPost is undefined when displayPosts is empty). This could cause issues with SEO meta tags and OpenGraph data. Consider using the original edges data for Layout props or provide fallback values.

Copilot uses AI. Check for mistakes.
Comment on lines 19 to 26
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts(edges || []);
setIsLoading(false);
}, 500);

return () => clearTimeout(timer);
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition "typeof window !== 'undefined'" is unnecessary in a useEffect hook. useEffect only runs on the client side, so window is always defined. This check should be removed to simplify the code.

Suggested change
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts(edges || []);
setIsLoading(false);
}, 500);
return () => clearTimeout(timer);
}
const timer = setTimeout(() => {
setDisplayPosts(edges || []);
setIsLoading(false);
}, 500);
return () => clearTimeout(timer);

Copilot uses AI. Check for mistakes.
Comment on lines 18 to 25
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts(edges || []);
setIsLoading(false);
}, 500);

return () => clearTimeout(timer);
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition "typeof window !== 'undefined'" is unnecessary in a useEffect hook. useEffect only runs on the client side, so window is always defined. This check should be removed to simplify the code.

Suggested change
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts(edges || []);
setIsLoading(false);
}, 500);
return () => clearTimeout(timer);
}
const timer = setTimeout(() => {
setDisplayPosts(edges || []);
setIsLoading(false);
}, 500);
return () => clearTimeout(timer);

Copilot uses AI. Check for mistakes.
pages/index.tsx Outdated
Comment on lines 22 to 32
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts({
community: communityPosts || [],
technology: technologyPosts || [],
});
setIsLoading(false);
}, 500);

return () => clearTimeout(timer);
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition "typeof window !== 'undefined'" is unnecessary in a useEffect hook. useEffect only runs on the client side, so window is always defined. This check should be removed to simplify the code.

Suggested change
if (typeof window !== "undefined") {
const timer = setTimeout(() => {
setDisplayPosts({
community: communityPosts || [],
technology: technologyPosts || [],
});
setIsLoading(false);
}, 500);
return () => clearTimeout(timer);
}
const timer = setTimeout(() => {
setDisplayPosts({
community: communityPosts || [],
technology: technologyPosts || [],
});
setIsLoading(false);
}, 500);
return () => clearTimeout(timer);

Copilot uses AI. Check for mistakes.
Comment on lines 9 to 14
className={cn(
"animate-pulse rounded-md bg-gray-200 dark:bg-gray-800",
className
)}
{...props}
/>
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skeleton loader lacks ARIA attributes to indicate loading state to screen readers. Add role="status" and aria-live="polite" attributes along with visually hidden text like "Loading content..." to improve accessibility for users with assistive technologies.

Suggested change
className={cn(
"animate-pulse rounded-md bg-gray-200 dark:bg-gray-800",
className
)}
{...props}
/>
role="status"
aria-live="polite"
className={cn(
"animate-pulse rounded-md bg-gray-200 dark:bg-gray-800",
className
)}
{...props}
>
<span className="sr-only">Loading content...</span>
</div>

Copilot uses AI. Check for mistakes.
Comment on lines 258 to 260
{loading && visibleCount === 0 ? (
<PostGridSkeleton count={12} />
) : (
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skeleton loader will never display because visibleCount is initialized to 12 on line 40 and is never set to 0. The condition "loading && visibleCount === 0" will always be false. Consider using a different condition, such as checking if this is the initial load or if allPosts is empty.

Copilot uses AI. Check for mistakes.
Signed-off-by: manthansubhash01 <manthanziman20@gmail.com>
Signed-off-by: manthansubhash01 <manthanziman20@gmail.com>
Signed-off-by: manthansubhash01 <manthanziman20@gmail.com>
@amaan-bhati
Copy link
Member

Hey @manthansubhash01 Thanks for raising the pr, preview looks good to me. Can you please take a look at the copilot review and the merge conflicts?

@manthansubhash01
Copy link
Author

manthansubhash01 commented Dec 30, 2025

@amaan-bhati
Thank you for the review. I’ve already made the changes suggested by Copilot and also resolved the conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants