-
Notifications
You must be signed in to change notification settings - Fork 27.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Next13.4 appDir does not render layout and template in the way the docs say it should #49596
Comments
+1. I've been dealing with this for months now. |
dont wanna be critical but now I see that introducing appdir thing showing hows flawed the architecture of nextjs . such minor feature as option to use appdir fiolder causing so many problems. |
I don't know why Next.js needs to be changed to this way |
@timneutkens any chance someone can look into this, or a status update? It's currently blocking the usage of exit animations with framer motion that I'd loveee to use in a website I'm working on right now. |
Would love if someone could look into this ^ |
EDIT: Moved the below into a discussion here: #56594. With the (IMO well-thought-out) way the app router handles templates and layouts, this is a bit finicky to implement. I'm just looking at the source using my own knowledge so please keep that disclaimer in mind, but here goes. As outlined above, to persist a route after its lifetime (which is key for exit transitions), we need to somehow bridge the gap between a persisted component a user can manage (where one could place Framer motion's Firstly, for a single layout with Secondly, the Thirdly, while currently each Internally, the DOM created looks like this (as illustrated in the issue itself): <...USER.Layout > {/* This is your custom layout */}\
{/* Some user components */}
<OuterLayoutRouter> {/* The OuterLayoutRouter for the parallel route */}
{styles /* If the root layout, e.g. with <html>, other styles go here */}
{preservedSegments.map(() => ( {/* As of Next.js 15.4.X, only one segment is preserved at a time. */}
<TemplateContext.Provider key="....">
<USER.Template>
<RenderTemplateContextValues> {/* This renders the contents inside the template */}
</USER.Template >
</TemplateContext.Provider>
)}
</OuterLayoutRouter>
{/* Maybe some more user components and parallel routes w/ their own OuterLayoutRouters */}
</...USER.Layout > As stated before, what we need is to be able to insert a User-defined component wrapping the My SolutionUsing the above information, a super quick way to implement such exit transition behavior is to create another Next.js file-based component. For my purposes, I've called this <...USER.Layout >
{/* Some user components */}
<OuterLayoutRouter>
{styles}
<USER.Glue > {/* <--- Placement of new "Glue" component */}
{preservedSegments.map(() =>
<TemplateContext.Provider key="....">
<USER.Template>
<RenderTemplateContextValues>
</USER.Template >
</TemplateContext.Provider>
)}
</USER.Glue >
</OuterLayoutRouter>
{/* More user components */}
</...USER.Layout > An example // app/[segment]/glue.tsx
'use client'
import React from 'react'
import { AnimatePresence } from 'framer-motion';
import { PropsWithChildren } from 'react'
export default function Glue({ children }: PropsWithChildren) {
return (
<AnimatePresence initial={false} mode="wait">
{children}
</AnimatePresence>
)
} With a corresponding template providing the // app/[segment]/template.tsx
"use client";
import { motion } from "framer-motion";
import React from "react";
import { PropsWithChildren } from "react";
export default function Template({ children }: PropsWithChildren) {
return (
<motion.div
initial={{ opacity: 0, dur: 1000 }}
animate={{ opacity: 1, dur: 1000 }}
exit={{ opacity: 0, dur: 1000 }}
>
{children}
</motion.div>
)
} Because of how Next parses Making the above changes gives the following results: Screen.Recording.2023-10-08.at.3.05.00.PM.mov(Note how the sidebar parallel route also receives exit transitions—though, confusingly, the transition occurs only when moving from the root The folder structure of the above example looks like this: BranchThe branch / draft PR with the above changes adding a MoreI think I speak for a lot of web designers / developers when I say that this is probably one of the largest issues keeping me on the |
Verify canary release
Provide environment information
Operating System: Platform: darwin Arch: arm64 Version: Darwin Kernel Version 22.4.0: Mon Mar 6 20:59:58 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6020 Binaries: Node: 18.16.0 npm: 9.5.1 Yarn: 1.22.19 pnpm: N/A Relevant packages: next: 13.4.2-canary.4 eslint-config-next: 13.4.1 react: 18.2.0 react-dom: 18.2.0 typescript: 5.0.4
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Routing (next/router, next/navigation, next/link)
Link to the code that reproduces this issue
https://codesandbox.io/p/sandbox/wizardly-wing-fj1kod
To Reproduce
Download and run the files in the sandbox (so that you can attach the React Developer Tools)
Note that in the react developer tools, the following component tree is rendered, showing the top
Suspense
added by thelayout.tsx
, two Next internal components, and then theSuspense
from thetemplate.tsx
:Describe the Bug
The NextJS Documentation on templates in the app-directory router here says that layouts and templates will be rendered as such:
However, in practice we can clearly see that NextJS inserts a component called
OuterLayoutRouter
between each layout-template pair, separating the layout from the part of the Template framework that receives the uniquekey
promised by the docs.This means that components that rely on detecting a change of key of their direct child cannot function in the layout-template lifecycle. In practice this leads to issues primarily with animation libraries, such as #49279.
At its core, though, I think the bug here is that the documentation and the behavior do not align with each other.
Expected Behavior
This could be fixed in one of two ways:
OuterLayoutRouter
inlayout-router.tsx
could be updated so that it receives thelayout.tsx
component from the app-render loop and embeds that layout in its returned JSX tree, which would allow the layout and template to truly be parent-child. I think this would be preferable but I'm also still wrapping my head around how the new app render loop works so I don't yet know what would might make this difficult.Which browser are you using? (if relevant)
Chrome but not relevant
How are you deploying your application? (if relevant)
Not relevant
NEXT-1380
The text was updated successfully, but these errors were encountered: