next.js
next.js copied to clipboard
Parent `loading.tsx` UI flashes prior to nested `loading.tsx` UI
Verify canary release
- [X] I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 21.6.0: Sat Jun 18 17:07:22 PDT 2022; root:xnu-8020.140.41~1/RELEASE_ARM64_T6000
Binaries:
Node: 16.14.2
npm: 8.5.0
Yarn: 1.22.19
pnpm: 7.13.4
Relevant packages:
next: 13.0.5-canary.4
eslint-config-next: 13.0.5-canary.4
react: 18.2.0
react-dom: 18.2.0
What browser are you using? (if relevant)
Chrome
How are you deploying your application? (if relevant)
No response
Describe the Bug
Perhaps this is me not fully understanding the intended behavior, but when I have nested loading.tsx
files, often the top-level loading will show when navigating from a top level route to a nested route in a way that feels janky / undesirable. Take the following app dir as an example:

In this scenario, I have a home page.tsx
which includes links to jokes/category
. The only fetches I make are in the root level page.tsx
and jokes/[id]/page.tsx
. When clicking a link from the home page to the nested joke, first the root loading.tsx
is rendered prior to the loading.tsx
defined in the jokes/[id]
. This causes an undesirable UI, with multiple loading states flashing, and it's not clear to me why the top level loading indicator renders if that part of the UI has already rendered / loaded.
You can see the behavior here:
https://user-images.githubusercontent.com/1236841/203194699-b61ad08d-8ce7-4eb8-b8ca-fc3ee87f94ed.mp4
Step by step, you can see that "Loading Root" renders briefly before "Loading nested" which renders before the requested fetch has finished.
Home (app/page.tsx):
Clicking a link first renders the root loading.tsx
:
And then renders the nested loading.tsx
:
Before finally rendering the nested route:
Expected Behavior
When clicking a link from the home page to jokes/[id]
I'd expect only the loading UI defined next to [id]
to render.
Link to reproduction - Issues with a link to complete (but minimal) reproduction code will be addressed faster
https://github.com/bmcmahen/next-13-nested-loading
To Reproduce
Create a loading.tsx
file at the root level of your app directory.
Fetch data in the root page.tsx
file.
Create a nested loading.tsx
file in jokes/[id]
.
Create a link in the root page.tsx
file to jokes/id
.
Run the dev server or build for production / host, and click a link. Notice that the root loading.tsx
file renders prior to the jokes/[id]
loading tsx.
will there be any fix for this, or is there another way of doing this? I am experiencing this too, but only for dynamic nested pages. and it creates a really bad user experience, especially when my parent's page skeleton differs a lot from the child's skeleton.
I'm also curious if there's a workaround for this, I'm having the difference in skeletons issue too like @RisKakaN described
Only workaround we've found is eliminating loading states everywhere but the leaves of our app. Feels pretty slow but not broken.
still havent found a proper workaround for this. hopefully there will be a fix.
I had the same experience and wonder if this should be considered a bug. Hopefully someone from the next team can have a look soon.
Still present in 13.5.5-canary.2
Loading ui is so close to perfect, but for me this flash is a dealbreaker for anything but leaf routes.
@leerob @balazsorban44 any updates regarding this? I could try to fix it myself if someone could point me in the right direction.
https://github.com/vercel/next.js/assets/11774195/521d3513-91ac-4632-ad51-eeea0a6520a2
Also struggling with this issue. Sometimes I want my nested loading.tsx to show, I never want the root to show if I have an even better, more specific skeleton, or else its a bad flash of content.
I deleted all of the loading.tsx files in my project until this issue gets resolved. For me, any loading.tsx under a dynamic route does not get loaded at all. It shows the nearest parent loading.tsx, and only that, no flashing.
There is no point in showing an irrelevant loading page to the user, it is just bad UX.
- I tried deleting the parent loading.tsx, that allows nested loadings properly, but I need a parent loading.tsx as well
- Also tried route grouping, that does not work.
The nearest parent loading.tsx to a dynamic route ALWAYS gets prioritized over any nested loading.tsx’s under that dynamic route
any hopes 🥲
This is still happening as of 14.1.0.
When hitting the leaf route directly on the server, only the leaf's loading page is shown (correct behaviour). However, when you first load the parent route, then navigate to the leaf page, the parent's loading page is also rendered.
I have confirmed that the nearest loading page is rendered when this happens. E.g.
app
├── layout.tsx
├── page.tsx
└── parent
└── [id]
├── layout.tsx
├── leaf
│ ├── [slug]
│ │ ├── loading.tsx
│ │ └── page.tsx
│ ├── loading.tsx <-- This will flash before loading from [slug] is shown.
│ └── page.tsx
├── loading.tsx <-- This will flash if leaf/loading.tsx is removed.
└── page.tsx
The only 'workaround' I've found is to put a dummy loading.tsx
below the leaf node. E.g. in this situation, if you change leaf/loading.tsx
to be a component which returns null, you'll get a flash of empty page, which is slightly better than a completely incorrect skeleton. Obviously this is painful if you're also showing a complex page at leaf/
as you're now left without any skeleton for that page.
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!
I've figured out a kind of workaround. You can use route groups (group)
to 'mirror' what would be a nested route, e.g:
app
├── (app)
│ ├── (leaves)
│ │ └── parents
│ │ └── [id]
│ │ └── leaf
│ │ └── [slug]
│ │ ├── loading.tsx
│ │ └── page.tsx
│ ├── (parents)
│ │ └── parent
│ │ └── [id]
│ │ ├── loading.tsx
│ │ └── page.tsx
│ └── page.tsx
├── favicon.ico
├── globals.css
├── layout.tsx
└── page.tsx
In this case, neither route has a parent loading.tsx, but they still render at the correct urls /parent/id
and /parent/id/leaf/slug/
.
Of course, if you put a top level loading.tsx
in /app
, they'll both flash that one instead.
Please fix 🥲 this is so vital and issue has been open since 2022. Continuing to bump here
+1
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!
Adding my voice to those experiencing this. Hopefully a fix is prioritised.
Experiencing the same thing, hope there is an update soon.
Hey folks! Going to do my best to explain what's happening here, specifically in the case where you're noticing it during next dev
or on links with prefetch={false}
:
- You load
/
. No prefetching happens, so we do not yet download the loading component(s) for the/jokes/<id>
links. - You click on
/jokes/1
. Since Next.js doesn't know about the loading component, it has to download the loading component from the server when you click on the link. While this is happening, it's going to fallback to the nearestSuspense
boundary that it knows about, in this case the "root"loading.tsx
component. - Once the loading boundary for
/jokes/1
is loaded, it switches to it, while waiting for the page data to come back from the server.
There are a couple of ways to fix this:
- Move the
loading.tsx
component a level higher in your folder hierarchy. For example, in the original post, the loading component is part ofapp/jokes/[id]
. But if we're trying to make a generic loading component to be shown when clicking any of thejokes
links, it should be part ofapp/jokes
instead. - If you can't change this hierarchy because you want
app/jokes
to have it's own loading component, you can leverage Route Groups to control this hierarchy. Here's an example of how the original repo could use route groups:
(without-root-loading)
layout.tsx
jokes
[id]
loading.tsx
page.tsx
(with-root-loading)
layout.tsx
loading.tsx
page.tsx
- Ensure that you have prefetching turned on so that they can be downloaded when the user loads the page. This will make it so that by the time the user clicks the link, it'll already have that loading state in the cache and won't need to suspend to download it from the server.
Here's a video where I've taken the original reproduction in the first post, and moved app/jokes/[id]/loading.tsx
to app/jokes/loading.tsx
:
https://github.com/vercel/next.js/assets/1939140/37211444-9ed8-4ad4-b382-9ebe25585af6
Please let me know if there's something I'm missing! Otherwise I will be closing this issue after some time of inactivity.
@ztanner amazing explanation, thank you 🙏
i came to the same conclusion with some experimentation but was wondering if this was intended behavior of the loading boundary in routes.
this really clears things up 🙌
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.