Next.js Discord

Discord Forum

NextJS Links are slow with AppRouter

Unanswered
Dwarf Crocodile posted this in #help-forum
Open in Discord
Dwarf CrocodileOP
Hi all, im in the process of rewriting my companies Casino apps to use nextjs from an old JS-Gatsby site. i was hoping to make use of the AppRouter but the user experience is terrible - i have a deployment here: https://d17whaw2emjvto.cloudfront.net/games/slots/bonus-round - and if you click on a link in the side panel it take a good 2-3 seconds for the page to start to redirect to where it's supposed to go. similarly if i use buttons with useRouter from next/navigation it takes 1-2 seconds before the page redirects.

the structure i have is that the sidebar (on desktop) and drop-down buttons (for the mobile view) are rendered in the Layout (which should do a request to get the categories), where the page is doing the requests for data for the games (via a fetch) and then applies any filters/sort on the data in the nextjs app.

pre-rendering the pages does not improve the speed, and caching doesn't change the 1st time render (and even then it only improves it by a second or so)...
So i guess my question is how can i make this faster?

23 Replies

Sun bear
my guess is that you are doing a really expensive operation server side, so when a user clicks a redirect link the server starts rendering the page, but due to the expensive operation it takes time, so the client waits till the page is ready to redirect the user
you can upgrade the ux by creating loading pages which are instantly showed to user upon navigation and serve as a way for users to understand the layout that will be loaded: https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
or optimize the expensive operation run on the server
Yakutian Laika
I agree, my project uses the app router and for some pages the transition is slow due to the amount of work the server has to do
I personally use unstable_cache by nextjs, it's pretty stable and you can reduce the server cost and load time for users
Dwarf CrocodileOP
i figured that and have attempted to cache them, using unstable_cache.

all the server does is request the API, and then apply sort/filter on it (the api itself is dumb af).

page has loading states already - but they only trigger when going from something like /category-a => /category-b but don't seem to trigger from /category-a/sub-cat-a => category-a/sub-cat-b
thing is, in the old site, everything was done on client-side; so was rather fast, this is much slower by comparison
Dwarf CrocodileOP
so i have a loading.tsx that looks like this:
function Skeleton({ className }: { className?: string }) {
  return (
    <div className={cn('animate-pulse rounded-md bg-gray-500', className)} />
  );
}

export function LoadingImpl() {
  return (
    <>
      <h1 className="mx-2 mb-2 text-xs font-bold"> loading... </h1>
      <div className="flex flex-col gap-2 md:flex-row md:flex-wrap">
        { 
          Array.from({ length: 10 }, (_, i) => (
            <Skeleton key={i} className="h-16 w-full md:h-[200px] md:w-[212px]" />
          ))
        }
      </div>
    </>
  );
}
  


i am also wrapping the children in the layout.tsx as a suspense boundary
place this file where your layout.tsx is
it will automatically add suspense to all routes nested in it
u dont need to add suspense in your layout.tsx
this is also not recommended
all your rsc components that are async and fetch data will now have a loading ui
Dwarf CrocodileOP
my LoadingImpl is exported as default from that file
i have removed the Suspense on the Layout (other than the ones needed where a client-component is using useSearchParams)

this is what my file tree looks like
Dwarf CrocodileOP
the main issue is what i mentioned earlier - there's no loading state between /category-a/sub-cat-a => category-a/sub-cat-b;

for example, using the link above, you can go from /games/slots/bonus-round to /games/slots/all via the sidebar; it takes time with no feedback that anything is happening.
Is adding loading.tsx inside [sub_category] directory helps ?
Dwarf CrocodileOP
i managed to get it working much quicker by using a catch-all route
and with some forcing of ISR its mostly acceptable now
Black carp
also keep in mind where you are doing your fetching. if you can move the data fetches lower in the tree wtih RSC, you'll get the static parts our first, allow for suspense to delay the actual UI you want to render.