Next.js Discord

Discord Forum

My nextJs app is slow

Answered
Axus posted this in #help-forum
Open in Discord
Hi,

I’m facing an issue with slow page load times in my Next.js project and I could use your help.

When navigating to a page (dashboard pages) , there is a noticeable delay before accessing the route. Even though I'm using lazy loading for some components, it still takes time to load after clicking a link, especially when fetching data from the API.

Could you share any advice or solutions to improve navigation speed and reduce this delay? Any help would be greatly appreciated.

If you need more details about the components or anything else, feel free to ask. I'd be happy to provide further information.

sidebar component essential:
"use client";

import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { cn } from "@/lib/utils";
import { Home, Check, Flower2, Trophy, BarChart2 } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";

const sidebarItems = [
  { name: "Habit", icon: Home, path: "/dashboard/my-habits" },
  { name: "Missions", icon: Check, path: "/dashboard/missions" },
  { name: "Garden", icon: Flower2, path: "/dashboard/garden" },
  { name: "Social", icon: Trophy, path: "/dashboard/social" },
  { name: "Stats", icon: BarChart2, path: "/dashboard/stats" },
];

export default function Sidebar() {
  const pathname = usePathname();

  return (
    <div className={cn("z-40 block h-full lg:w-64 bg-background border-r")}>
      <ScrollArea className="h-[calc(100vh-12rem)] p-6">
        {sidebarItems.map((item) => (
          <Button
            key={item.name}
            variant={pathname === item.path ? "default" : "ghost"}
            asChild
          >
            <Link href={item.path}>
              <item.icon className="size-4" />
              <span className="hidden md:block">{item.name}</span>
            </Link>
          </Button>
        ))}
      </ScrollArea>
    </div>
  );
}


lighthouse performace: 63
Thanks a lot
Answered by luis_llanes
For the “server side awaits” you’re doing, check if they really need to be awaited.. if you NEED the data they return to use in a following function or to display conditionally some UI or redirect the user, etc, then yes, you will have to await them.

In case of the user fetching and all that, that makes sense to await


But in case you just await to get the data and then pass that data down to another component, well you actually don’t need to await it, you’re making it wait for something that’s not urgent so you can simple let the component that needing that data to get it on its own, and you can let me patent component render a fallback while the child component is being slow, just by wrapping that specific component in its own suspense
View full answer

87 Replies

I don’t think the problem lies in the Sidebar component, I want to believe that component is rendered in the common layout between all those pages.
Seems like the problem has to do with slow fetching when you request the component and the component makes a async request that takes time.

I would first try to:
- add a loading.tsx next to where you have that layout.tsx, this will create a <Suspense> boundary around your page and display the fallback immediately instead of waiting for the component to be ready.
- you later can add fine grained <Suspense> than only wrap the specific slow component so you’re not compromising the rest of the UI that doesn’t need to suspend
Would be nice to see what the slow pages’ component looks like, and btw, you said only the “dashboard” pages are slow?

I think they’re always re-calculating and re-rendering on the server before they’re sent to the client which makes them slow
All components of the dashboard are slow, but they follow the same structure (server component with client-side children), and the largest one takes about 72 seconds to compile. I'm going to send you the slowest one. After implementing Suspense, I noticed a significant improvement, but there is still some delay. I also feel that the more we navigate through the pages, the faster it gets. Additionally, when I click on a link, there's a delay because the page has to be compiled before it's rendered.
When i said “dashboard” pages are slow, i mean that only the dashboard have this problem of navigation
------------------------------------------------------------------------------------------------------------------------------------------
Parent Component:
What you say makes sense but I’m not sure you know WHY:

- “the more we navigate through the pages, the faster it gets”, yes, and this might not even completely be a Next.js thing, the browser can cache the resources that are common and reused by many pages so you don’t have to request them again, the more you navigate the more the browser (and sometimes Next.js if you configure it to) will cache and serve from the cache instead of requesting it again

- “only the dashboard have this problem of navigation”, this probably is because dashboard pages are dynamic, which means they’re always being requested and rendered on the server every time you click a link to navigate there, and you other pages (say landing page or about-me page) can be statically generated at build time which won’t take extra time to show on the browser because they will be generated once and served again and again
import HabitDisplay from "@/components/asset_app/HabitDisplay";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { generateHeatmapData, getUserHabits, isRestDay } from "@/lib/actionHabit";
import { currentUser } from "@clerk/nextjs/server";
import { ArrowUpRight, CalendarDays } from "lucide-react";

export default async function HabitPage() {
  const user = await currentUser();
  const userDb = await getUser(user?.id as string);
  const userHabits = await getUserHabits(userDb?.id as string);


  const filteredDaily = userHabits.filter((item) => item.type !== "WEEKLY");
  const filteredWeekly = userHabits.filter((item) => item.type !== "DAILY");

  return (
    <section className="flex flex-col gap-4">
      <Card>
        <CardHeader><CardTitle>Habit Overview</CardTitle></CardHeader>
        <CardContent className="space-y-4">
          {filteredDaily.map(async (habit) => (
            <HabitDisplay
              key={habit.id}
              initialHabit={habit}
              initialRestDay={await isRestDay(habit.id, userDb.id)}
              initialHeatmapData={await generateHeatmapData(habit.id)}
            />
          ))}
          <div className="my-5 text-lg font-medium text-foreground">WEEKLY</div>
          {filteredWeekly.map(async (habit) => (
            <HabitDisplay
              key={habit.id}
              initialHabit={habit}
              initialRestDay={await isRestDay(habit.id, userDb.id)}
              initialHeatmapData={await generateHeatmapData(habit.id)}
            />
          ))}
        </CardContent>
      </Card>
    </section>
  );
}
Habit Display Component (cut):
Habit Display Component and Heatmap also
Oh wow you’re making so many fetches and awaiting so many of them, that’s certainly what’s making this page slow asf.

The page will not be sent to the client until ALL AWAITED CALLS are resolved, and if you don’t need that information right away to start showing the page it will only make it slow for no reason.
The first component properly uses Suspense and dynamic; it's just that it comes from GitHub.
Yes but that suspense will wrap the complete component not each sub component independently
By any chance, are you using the latest version of React? React 19?
I switched to Next.js 15 thinking that Turbopack would handle all the issues, but it didn't. You’re not reassuring me at all 😂, this component has the least await
@Axus Oww
This page component (and all the others that behave similarly) will have to go line by line awaiting each function call before proceeding to the next one, let’s say each “await fakeFunction()” takes one second to resolve and get the data, now you’re making react to wait 1 second for each of them, even the ones inside the “ return JSX “

Since it’s a server component it will only get to the client once it’s fully rendered
@Axus I switched to Next.js 15 thinking that Turbopack would handle all the issues, but it didn't. You’re not reassuring me at all 😂, this component has the least await
Wait that’s good tho, you have a composition issue it’s not next js or turbo pack it’s just the way JavaScript and react work
So the solution is to switch to a client-size component ? There are parent server-side component with +10 await
I can help you, but first you need to understand what the problem is so I’m kinda making sure we’re on the same page.

You know you can suspend the rendering of a component wrapping it in <Suspense> right? The component will start rendering in the background and while it’s getting its data and taking time it will display the fallback.

In react 19 you can now pass a promise and let the client component resolve the promise and suspend.
Instead of awaiting the call on the server component, DON’T await it, just pass the promise

Example:
Remove the await keyword and now your client component will receive the promise that will return the data instead of receiving the data ready
In your client component do this:

const data = use(initialHeatmapDataPromise)

This will offload the data loading to that client component and if you wrap this component in a suspense it’ll take less time because it’ll display the fallback instead of waiting for the full map to each resolve the awaited function before continuing rendering
ok, for example with this page , i can remove all the await and i need to transform in client-side component ?
(import removed)
export default async function MissionsComponents() {
  /// remove all await 
  const { userId } = await auth();

  if (!userId) {
    return Rnull
  }
  const user = await getUser(userId);
  await checkAchievementComplete(user?.id as string);
  const achievements = await getAllUserAchievements(user?.id as string);
  const streakDataValue = await getStreakFourDaysAgo(user?.id as string);
  const checkInsOfToday = await countCompletedHabitToday(user?.id as string);
  const questOfUser = await userQuest(user?.id as string);
  if (!questOfUser) {
    await generateDailyQuest(user?.id as string);
  }
  const habitOfUser = await getUserHabits(user?.id as string);

  return (
    <Suspense fallback={<Loading />}>
      <ContextProvider initialAchievements={achievements}>
        <div className="flex flex-col gap-4 size-full">
          <div className="flex flex-row gap-4 w-full">
            <DailyQuests
              initialQuest={questOfUser}
              initialHabit={habitOfUser}
              initialCheckIns={checkInsOfToday}
              userId={user?.id as string}
            />
            <StreakComponents initialStreakData={streakDataValue} />
          </div>
          <AchievementsComponent />
        </div>
      </ContextProvider>
    </Suspense>
  );
}
i will try this right now
But make sure to type the new component prop as a Promise#Unknown Channel because it’s now a promise, not longer the data
The use() API:
const data = use(dataPromise)

works just like:
const data = await dataFn()
The thing is use() needs to get a promise as an argument, you can’t pass the function
so this page become ?
import AchievementsComponent from "@/components/asset_mission/AchievementsComponent";
import DailyQuests from "@/components/asset_mission/DailyQuests";
import StreakComponents from "@/components/asset_mission/StreakComponent";
import { ContextProvider } from "@/hooks/use-achievement";
import {
  checkAchievementComplete,
  getAllUserAchievements,
} from "@/lib/actionAchievement";
import { countCompletedHabitToday, getUserHabits } from "@/lib/actionHabit";
import { generateDailyQuest, userQuest } from "@/lib/actionQuest";
import { getStreakFourDaysAgo } from "@/lib/actionStreak";
import { getUser } from "@/lib/actionUser";
import { auth } from "@clerk/nextjs/server";
import { Suspense } from "react";
import Loading from "./loading";

export default function MissionsComponents() {
  const { userId } = await auth();

  if (!userId) {
    return null;
  }

  const user = getUser(userId); 
  const achievements = getAllUserAchievements(user?.id as string);  
  const streakDataValue = getStreakFourDaysAgo(user?.id as string); 
  const checkInsOfToday = countCompletedHabitToday(user?.id as string); 
  const questOfUser = userQuest(user?.id as string);  

  
  if (!questOfUser) {
    generateDailyQuest(user?.id as string);
  }

  const habitOfUser = getUserHabits(user?.id as string); 

  return (
// these component should all be client
    <Suspense fallback={<Loading />}>
      <ContextProvider initialAchievements={achievements}>
        <div className="flex flex-col gap-4 size-full">
          <div className="flex flex-row gap-4 w-full">
            <DailyQuests
              initialQuest={questOfUser}
              initialHabit={habitOfUser} 
              initialCheckIns={checkInsOfToday} 
              userId={user?.id as string}
            />
            <StreakComponents initialStreakData={streakDataValue} /> 
          </div>
          <AchievementsComponent />  
        </div>
      </ContextProvider>
    </Suspense>
  );
}
i am right ?
For the “server side awaits” you’re doing, check if they really need to be awaited.. if you NEED the data they return to use in a following function or to display conditionally some UI or redirect the user, etc, then yes, you will have to await them.

In case of the user fetching and all that, that makes sense to await


But in case you just await to get the data and then pass that data down to another component, well you actually don’t need to await it, you’re making it wait for something that’s not urgent so you can simple let the component that needing that data to get it on its own, and you can let me patent component render a fallback while the child component is being slow, just by wrapping that specific component in its own suspense
Answer
@Axus so this page become ? tsx import AchievementsComponent from "@/components/asset_mission/AchievementsComponent"; import DailyQuests from "@/components/asset_mission/DailyQuests"; import StreakComponents from "@/components/asset_mission/StreakComponent"; import { ContextProvider } from "@/hooks/use-achievement"; import { checkAchievementComplete, getAllUserAchievements, } from "@/lib/actionAchievement"; import { countCompletedHabitToday, getUserHabits } from "@/lib/actionHabit"; import { generateDailyQuest, userQuest } from "@/lib/actionQuest"; import { getStreakFourDaysAgo } from "@/lib/actionStreak"; import { getUser } from "@/lib/actionUser"; import { auth } from "@clerk/nextjs/server"; import { Suspense } from "react"; import Loading from "./loading"; export default function MissionsComponents() { const { userId } = await auth(); if (!userId) { return null; } const user = getUser(userId); const achievements = getAllUserAchievements(user?.id as string); const streakDataValue = getStreakFourDaysAgo(user?.id as string); const checkInsOfToday = countCompletedHabitToday(user?.id as string); const questOfUser = userQuest(user?.id as string); if (!questOfUser) { generateDailyQuest(user?.id as string); } const habitOfUser = getUserHabits(user?.id as string); return ( // these component should all be client <Suspense fallback={<Loading />}> <ContextProvider initialAchievements={achievements}> <div className="flex flex-col gap-4 size-full"> <div className="flex flex-row gap-4 w-full"> <DailyQuests initialQuest={questOfUser} initialHabit={habitOfUser} initialCheckIns={checkInsOfToday} userId={user?.id as string} /> <StreakComponents initialStreakData={streakDataValue} /> </div> <AchievementsComponent /> </div> </ContextProvider> </Suspense> ); }
Well the user data needs to exist for the next line so that one NEEDS to be awaited
Once you understand when awaiting a call is necessary, and when it’s not you’ll come up with different solutions depending on the use-case you’re having
if i want the data to display it, i need to await them ? Because i need most of these data to be displayed
for example :achievement streakdata etc etc need to be displayed
Think of it this way:

if I need the data this function returns because I will use it in a following function or I need to conditionally do something depending of the result of this function then I NEED to await it
Awaiting it is the only way you’re getting the data back before you continue to the next line of code
And think about not awaiting like:

“if I need to kick off or start this fetching but I don’t need the result back yet, because it’s not urgent or I won’t use its return data to conditionally do stuff next, I can just call it without “await” and this will return the promise, not the data.

Preference: in the cases you don’t await the call, you should name the variable “<name>Promise” to know exactly that that variable doesn’t hold the actual data, it hold the promise that once is “unwrapped” will return the data

const dataPromise = fn()
const data = await fn()
wow
here is the page, any suggestion ?
Wait let me open that on my PC, but that’s a HUGE improvement already!
That already looks so much cleaner and better.
I wonder what was the reason for you to do this:


dynamic() is the equivalent to wrapping the component in React.lazy i believe, that helps the component trigger the suspense boundary.
if you're using the use(promise) API in that component this should not be necessary tho.

use(promise) already suspends the component and it triggers the suspense boundary
If a page is heavy, does she influence other pages of the app ?
I don't think this <Suspense> is needed. You're only displaying the "no content" right? I bet that won't trigger the skeleton, unless your callendar is async and triggers it
@Axus If a page is heavy, does she influence other pages of the app ?
Oh you mean it does not need to load until it reaches the second return?
if the page stops here if (!userHabits?.length) it wont even get to the point of rendering the <HabitDisplay > component.. makes sense
@luis_llanes Oh you mean it does not need to load until it reaches the second return? if the page stops here if (!userHabits?.length) it wont even get to the point of rendering the <HabitDisplay > component.. makes sense
For the moment I've only optimized the my habit page, the others not yet, so I'm wondering if it has any impact on the loading of this page.
By the way, there's another “Topbar” component that's part of the layout and fetches little data
import { getUserHabits } from "@/lib/actionHabit";
import { getUser } from "@/lib/actionUser";
import { auth } from "@clerk/nextjs/server";
import { Diamond, PlusIcon, User } from "lucide-react";
import Link from "next/link";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import { Button } from "../ui/button";

export default async function Topbar() {
  const { userId } = await auth();

  const userDb = await getUser(userId as string);
  const allUserHabit = await getUserHabits(userDb?.id as string);

  return (
    <div className="flex w-full items-center justify-around border-b px-2 py-3">
      <div className="flex items-center gap-2">
        <h2 className="font-medium">{allUserHabit.length} HABIT</h2>
        <a href="/habitAction/add">
          <Button variant="outline" size="icon" asChild>
            <PlusIcon className="text-gray-400" strokeWidth={1.5} size={20} />
          </Button>
        </a>
      </div>
      <div className="flex flex-row items-center justify-center gap-2">
        <Avatar>
          <AvatarImage src={userDb?.image ?? ""} alt="profile picture" />
          <AvatarFallback>{userDb?.name}</AvatarFallback>
        </Avatar>
        <div className="flex items-center gap-2 bg-white px-4 py-2 rounded-full shadow-sm">
          <Diamond className="w-5 h-5 text-blue-500" />
          <span className="font-medium">{userDb?.gems as number}</span>
        </div>

        <Button
          asChild
          size="default"
          variant="ghost"
          className="flex items-center gap-2"
        >
          <Link
            href={{
              pathname: "/profile/my-profile",
              query: { userId },
            }}
          >
            <User className="h-4 w-4" />
            Profile
          </Link>
        </Button>
      </div>
    </div>
  );
}
@Axus For the moment I've only optimized the my habit page, the others not yet, so I'm wondering if it has any impact on the loading of this page.
I don’t think so, as long as the component is wrapped in its Suspense boundary it should impact the performance meaningfully.
@Axus tsx import { getUserHabits } from "@/lib/actionHabit"; import { getUser } from "@/lib/actionUser"; import { auth } from "@clerk/nextjs/server"; import { Diamond, PlusIcon, User } from "lucide-react"; import Link from "next/link"; import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; import { Button } from "../ui/button"; export default async function Topbar() { const { userId } = await auth(); const userDb = await getUser(userId as string); const allUserHabit = await getUserHabits(userDb?.id as string); return ( <div className="flex w-full items-center justify-around border-b px-2 py-3"> <div className="flex items-center gap-2"> <h2 className="font-medium">{allUserHabit.length} HABIT</h2> <a href="/habitAction/add"> <Button variant="outline" size="icon" asChild> <PlusIcon className="text-gray-400" strokeWidth={1.5} size={20} /> </Button> </a> </div> <div className="flex flex-row items-center justify-center gap-2"> <Avatar> <AvatarImage src={userDb?.image ?? ""} alt="profile picture" /> <AvatarFallback>{userDb?.name}</AvatarFallback> </Avatar> <div className="flex items-center gap-2 bg-white px-4 py-2 rounded-full shadow-sm"> <Diamond className="w-5 h-5 text-blue-500" /> <span className="font-medium">{userDb?.gems as number}</span> </div> <Button asChild size="default" variant="ghost" className="flex items-center gap-2" > <Link href={{ pathname: "/profile/my-profile", query: { userId }, }} > <User className="h-4 w-4" /> Profile </Link> </Button> </div> </div> ); }
Is the TopBar component slowing down your app?
I don't think you should worry too much about it, but you could still wrap that component in <Suspense> in the layout for the first render to display the static layout contents while this dynamic component still fetches the data
Let me know how it ends up performing when you fix the other pages, just remember the pattern of awaiting or not awaiting data as needed
@luis_llanes Let me know how it ends up performing when you fix the other pages, just remember the pattern of awaiting or not awaiting data as needed
Ye, i "await" only if another function need this data or if it is about user action (protecting route)
Or if you’re gonna return some JSX based on that info, or redirecting to another route right away, like if the user isn’t authenticated
@luis_llanes Exactly
Im trying to do that with another page but for every changes i make, it is getting worse. I tried many combination but i can't get over 50+ performance, It is a stats page
@Axus Im trying to do that with another page but for every changes i make, it is getting worse. I tried many combination but i can't get over 50+ performance, It is a stats page
Let’s see what you’re having issues with, mind to share some code snippets? (If you haven’t solved it)
I don't need general advice but rather feedback on my coding approach. For instance, in my components, each child receives props from the parent (so the page is rendered with data). Is this a good practice, or should each child component fetch its own data instead? Also, I feel like I’m not using server/client components properly.

One thing that bothers me is this repeated pattern in multiple components:
const user = await currentUser();
const userDb = await getUser(user?.id as string);
@Axus I don't need general advice but rather feedback on my coding approach. For instance, in my components, each child receives props from the parent (so the page is rendered with data). Is this a good practice, or should each child component fetch its own data instead? Also, I feel like I’m not using server/client components properly. One thing that bothers me is this repeated pattern in multiple components: tsx const user = await currentUser(); const userDb = await getUser(user?.id as string);
If the data is only ever needed for that component, and it does not require any external data (like an Id, or whatever that’s coming from props) , and it’s a server component let it fetch its own data.

If it’s a client component the best practice as in Next.js 15 I to kick off the async operation in the parent and pass down the promise to the client-side child component, the child component uses “use(promise)”.

In both approaches you should wrap the component in <Suspense> so you don’t have to wait for it to be ready to stream the HTML
parent :
import DailyMonitoring from "@/components/asset_stats/DailyMonitoring";
import QuickStatsComponent from "@/components/asset_stats/QuickStats";
import WeeklyMonitoringComponent from "@/components/asset_stats/WeeklyMonitoring";
import { generateDailyMonitoringData } from "@/lib/actionHabit";
import { generateChartWeeklyData, getQuickStatsData } from "@/lib/actionStats";
import { getUser } from "@/lib/actionUser";
import { currentUser } from "@clerk/nextjs/server";
import { Suspense } from "react";
import Loading from "./loading";

export default async function StatisticsPage() {
  const user = await currentUser();
  const userDb = await getUser(user?.id as string);

  const QuickStatsData = getQuickStatsData(userDb?.id as string);
  const logsData = generateDailyMonitoringData(user?.id as string);
  const chartData = generateChartWeeklyData(userDb?.id as string);

  // i tried many things, like putting all the following components in server/client etc etc but nothing made them faster
  return (
    <Suspense fallback={<Loading />}>
      <div className="grid grid-cols-1 gap-4">
        <QuickStatsComponent initialQuickStats={QuickStatsData} />
        <DailyMonitoring initialLogs={logsData} />
        <WeeklyMonitoringComponent initialChartData={chartData} />
      </div>
    </Suspense>
  );
}
Well there if 1 component finishes in 50 milliseconds but the others take 3s or 5s, all 3 will show up until the slowest finished
@luis_llanes If the data is only ever needed for that component, and it does not require any external data (like an Id, or whatever that’s coming from props) , and it’s a server component let it fetch its own data. If it’s a client component the best practice as in Next.js 15 I to kick off the async operation in the parent and pass down the promise to the client-side child component, the child component uses “use(promise)”. In both approaches you should wrap the component in <Suspense> so you don’t have to wait for it to be ready to stream the HTML
ok so this turns into this:
import DailyMonitoring from "@/components/asset_stats/DailyMonitoring";
import QuickStatsComponent from "@/components/asset_stats/QuickStats";
import WeeklyMonitoringComponent from "@/components/asset_stats/WeeklyMonitoring";
import { generateDailyMonitoringData } from "@/lib/actionHabit";
import { generateChartWeeklyData, getQuickStatsData } from "@/lib/actionStats";
import { getUser } from "@/lib/actionUser";
import { currentUser } from "@clerk/nextjs/server";
import { Suspense } from "react";
import Loading from "./loading";

export default async function StatisticsPage() {

  return (
    <Suspense fallback={<Loading />}>
      <div className="grid grid-cols-1 gap-4">
        <QuickStatsComponent />
        <DailyMonitoring />
        <WeeklyMonitoringComponent />
      </div>
    </Suspense>
  );
}
QuickStatsComponent:
export default async function QuickStatsComponent() {
  const { userId } = await auth()
  const user = await getUser(userId as string);
  const data = await getQuickStatsData(user?.id);
  const stats = [
    {
      name: "Active Habits",
      value: data?.activeHabits,
      icon: Activity,
    },
    { name: "Current Streak", value: data?.finalStreak, icon: Award },
    {
      name: "Completion Rate",
      value: `${data?.habitCompletionRate}%`,

      icon: TrendingUp,
    },
    {
      name: "Total Check-ins",
      value: data?.totalCheckIns,
      icon: Zap,
    },
  ];

  return (
    <Card className="w-full">
      <CardHeader>
        <CardTitle className="text-center">Quick Stats</CardTitle>
      </CardHeader>
      <CardContent className="grid grid-cols-2 md:grid-cols-4 gap-4">
        {stats.map((stat, index) => (
          <Card key={index}>
            <CardContent className="flex flex-col items-center justify-center p-6">
              <stat.icon className="size-6" />
              <h3 className="mt-2 text-sm font-medium text-gray-900">
                {stat.name}
              </h3>
              <p className="mt-1 text-2xl font-semibold text-gray-700">
                {stat.value}
              </p>
            </CardContent>
          </Card>
        ))}
      </CardContent>
    </Card>
  );
}
all the child are server
@luis_llanes Well there if 1 component finishes in 50 milliseconds but the others take 3s or 5s, all 3 will show up until the slowest finished
The suspense boundary needs to wait until the slowest component is ready to display anything that’s wrapped in the same <Suspense>

If you want each to stream down as soon as they’re ready, then each must be wrapped in their own Suspense
but the problem is that repetitive :
  const { userId } = await auth()
  const user = await getUser(userId as string);
Also, if that component is rendered inside the page and you already did the check on the parent you don’t need that check in the child components.

The check should be done once per route not once per component
Because the user can’t go straight into the component while navigating in the URL
It navigates to the page, and the page decides either to render and show the components or not
You’re doing the same call for data you already have
@Axus parent : tsx import DailyMonitoring from "@/components/asset_stats/DailyMonitoring"; import QuickStatsComponent from "@/components/asset_stats/QuickStats"; import WeeklyMonitoringComponent from "@/components/asset_stats/WeeklyMonitoring"; import { generateDailyMonitoringData } from "@/lib/actionHabit"; import { generateChartWeeklyData, getQuickStatsData } from "@/lib/actionStats"; import { getUser } from "@/lib/actionUser"; import { currentUser } from "@clerk/nextjs/server"; import { Suspense } from "react"; import Loading from "./loading"; export default async function StatisticsPage() { const user = await currentUser(); const userDb = await getUser(user?.id as string); const QuickStatsData = getQuickStatsData(userDb?.id as string); const logsData = generateDailyMonitoringData(user?.id as string); const chartData = generateChartWeeklyData(userDb?.id as string); // i tried many things, like putting all the following components in server/client etc etc but nothing made them faster return ( <Suspense fallback={<Loading />}> <div className="grid grid-cols-1 gap-4"> <QuickStatsComponent initialQuickStats={QuickStatsData} /> <DailyMonitoring initialLogs={logsData} /> <WeeklyMonitoringComponent initialChartData={chartData} /> </div> </Suspense> ); }
I like this approach better because the 3 of them depend on the same data,

if the child components you are rendering are client components then try to kick off (start) the async operations at the top of the parent component and pass down the promise to the child (client) component

If the child component is also a server component, you could simply pass the user object or whatever it needs to fetch and resolve its own data.

Either way wrap each child in its own suspense
i finished to optimize stats and social page
my code became way better with your advice
, now i need to optimize 2 other
@Axus my code became way better with your advice
I’m glad that worked!
When it’s finished let me know, honestly 72 seconds was crazy haha