#Why is there a delay between when the __root.tsx layout loads and the <Outlet />?

108 messages · Page 1 of 1 (latest)

spark oriole
still valve
#

Do you have a beforeLoad with any asynchronous data?

spark oriole
leaden temple
#

cannot access the repo

spark oriole
minor bone
#

fwiw, this does happen to me too on my own project. I use beforeLoad but I've tested without it and same issue. It's just the root layout. route.tsx ones dont have this issue. Wasn't a problem pre-RC

minor bone
# leaden temple reproducer?

Would love to but yea it's a massive repo. Just wanted to mention it in case it was helpful to like realize its not a singular issue with OP. It's not a big deal for me personally, but noticed it for sure.

I will see if i can make a small repro, I'll lyk

leaden temple
minor bone
#

Checking. It shows as <template> first (and doesn't show on the page) and then changes to a div. Sorry it's an image but this happens so fast, I had to screen record and then screenshot from the video lol

#

Yea, and then in the SSR response, the content is actually in a <div hidden id="S:0"> at the bottom.

I'm going to try to make a fresh small repro with this issue, i'll lyk

leaden temple
#

you should be able to inspect the SSR response html (view source instead of inspect document )

spark oriole
minor bone
#

Yea it's also a potential SEO concern. I'm checking and doing some debugging, will lyk

minor bone
#

Still digging, at this point not sure if it's a TS or React issue, but using a very simple ArtCard component version of what ikraam shared

import { Link } from "@tanstack/react-router";
import type { ArtPiece } from "data/art-pieces";
import { useState } from "react";
import { cn } from "utils/cn";

export function ArtCard({ artPiece }: { artPiece: ArtPiece }) {
  const [isHovered, setIsHovered] = useState(false);

  return (
    <article
      className="group relative"
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <Link
        to="/art/$id"
        params={{ id: artPiece.id }}
        aria-label={`View details for ${artPiece.title} by ${artPiece.artist}`}
        className="block"
      >
        <div className="relative overflow-hidden bg-neutral-100">
            <div className="relative h-full w-full">
              <img
                src={artPiece.imageUrl}
                alt={`${artPiece.title}oooooOoooooo`}
                className={cn(
                  "block h-full w-full object-cover transition-opacity duration-300"
                )}
              />
            </div>
        </div>
      </Link>
    </article>
  );
}

adding one more single o letter in that image alt tag will make the page do that whole streaming template thing, if I keep the same number of os, works fine. I don't know if that's due to byte size, or just somewhat of a race condition in terms of what finishes before the other (layout vs. body).

but yea, digging through, just sharing findings

spark oriole
#

very strange, I'll change the alt and push

#

that didn't change anything. That template element is from React suspense right?

minor bone
spark oriole
minor bone
spark oriole
#

I think the issue is the fact I had other components in the root layout instead of just the Outlet. I created a new file with a pathless layout for the header and footer and the outlet content loads instantly even though you can still see the <template/> tag

#

very strange

still valve
#

What happens if you change component: RootComponent to shellComponent: RootComponent ?

#

Ah yes, shellComponent must house your <html> tag, pretty sure

minor bone
#

Believe that does the opposite of what we're trying to do -- might be wrong.

leaden temple
#

so this only occurs with react 19.2

#

not with 19.1

still valve
leaden temple
#

not sure yet. will talk with react team

spark oriole
#

I worked around this issue by moving these components to another layout file, I think having these extra components in the root layout was causing the delayed load time for the Outlet:

function RootDocument({ children }: { children: ReactNode }) {
  return (
    <html lang="en" className="overscroll-none antialiased">
      <head>
        <HeadContent />
      </head>
      <body>
        <Navigation />
        {children}
        <AcquirePieces />
        <Footer />
        <Toaster position="top-center" richColors closeButton />
        <Scripts />
      </body>
    </html>
  );
}
#

Now I changed it to this:

function RootDocument({ children }: { children: ReactNode }) {
  return (
    <html lang="en" className="overscroll-none antialiased">
      <head>
        <HeadContent />
      </head>
      <body>
        {children}
        <Scripts />
      </body>
    </html>
  );
}
still valve
#

Can you try the default setup of setting shellComponent to RootDocument with no {children} props, do you still encounter this issue? I cloned your repo and checked out the commit from yesterday and wasn't able to reproduce your flicker locally

spark oriole
# still valve Can you try the default setup of setting shellComponent to RootDocument with no ...

Flicker happens locally on dev if you spam refresh the browser you can sometimes see it. We you deploy it you'lll see it more often.

Here's the preview using the shellComponent and ssr: false https://ikraam-test--sovereign-atelier.netlify.app/

following this page: https://tanstack.com/start/latest/docs/framework/react/guide/selective-ssr#how-to-disable-ssr-of-the-root-route

What is Selective SSR? In TanStack Start, routes matching the initial request are rendered on the server by default. This means beforeLoad and loader are executed on the server, followed by rendering...

still valve
#

Do you need SSR to be false?

#

SSR should be true

#

The only thing I was saying to try was to change "component" to "shellComponent", the root layout with <html> should always be shellComponent, and without SSR you will get a white screen before render

spark oriole
still valve
#

In this link i'm just seeing font and styles flickering, but the layout seems correct

#

ah wait I see now

#

Which commit is this?

#

Seems like Safari and Chrome are loading it differently, Safari flickers in the fonts and styles with all the layouts loaded, but Chrome has the issue where the Outlet is delayed

spark oriole
#

I guess that’s another issue.

spark oriole
leaden temple
# leaden temple so this only occurs with react 19.2

you might not have seen my message above. there is an issue with react 19.2 which either is a bug in react or we need to fix this in start. we are waiting for react maintainers to get back to us about this. as soon as I know more I will let you know

spark oriole
leaden temple
#

yes

spark oriole
#

It's when you have any other component beside the <Outlet/> in the root layout

leaden temple
#

in the gallery example it is fixed for me

#

with 19.1

spark oriole
leaden temple
#

i specifically checked the returned SSR HTML for <div hidden id="S:0">

#

it exists with 19.2 but not with 19.1

spark oriole
#

so it doesn't have that problem with the outlet taking a split second to load it

spark oriole
still valve
#

This is only happening on prod right? Not dev ?

leaden temple
#

you have "react": "^19.1.0",

#

which will install react 19.2.2

#

notice the ^

#

remove that and reinstall

spark oriole
#

ah good catch

#

thanks

#

just have an issue with the playfair font taking a split second to load in lol

thin siren
minor bone
#

Yep, pretty sure that's the one. #1431549315809284146 message like i mentioned here, it literally took an extra letter to trigger the suspense

thin siren
#

So once the shell hits ~12.5 KB then any Suspense boundary larger than 500 bytes triggers this behaviour

#

And the native suspense boundary fallback is basically empty (<!--$?--> + hidden <div>) so that is expected that the client sees an empty SSR output until hydration finishes

#

ugh

minor bone
#

Wonder if there's a way to opt-out of that

thin siren
#

there is, you can do this:

renderToPipeableStream(<App />, {
  progressiveChunkSize: Number.POSITIVE_INFINITY, // or just a very large number
});
minor bone
#

Testing this out rn, let's see

thin siren
#

lmk, i'm out n about so i'd be v interested

minor bone
#
export const renderRouterToStream = async ({
  request,
  router,
  responseHeaders,
  children,
}: {
  request: Request
  router: AnyRouter
  responseHeaders: Headers
  children: ReactNode
}) => {
  if (typeof ReactDOMServer.renderToReadableStream === 'function') {
    const stream = await ReactDOMServer.renderToReadableStream(children, {
      signal: request.signal,
      nonce: router.options.ssr?.nonce,
      progressiveChunkSize: Number.POSITIVE_INFINITY
    })```

So yea, this worked in `renderRouterToStream.tsx`
#

Solved the issue for me

#

Can submit a PR for this but feels like a blanket fix? Not sure

thin siren
thin siren
#

for next.js?

minor bone
#

Yea

thin siren
#

I can't see how I can set progressiveChunkSize in Next.js conf

thin siren
#

this is just their fork of react dom

minor bone
#

Ah interesting. So yea does seem to be 12,800

thin siren
#

well i mean that's the default value

#

that's what it's hardcoded to in facebook/react repo too

#

hmm

minor bone
#

Oh, it was 1024 in the commit you linked

#

In some places, at least

#

I wonder how this plays with allReady though, I assuuuume it should be ignored?

thin siren
#

its always been 12,800 for the chunk size. the commit i linked is specifically the part that makes small boundaries get added together for the consideration which is what broke it for me

minor bone
#

got it, thanks for the explanation yea

thin siren
#

the only 1,024 is a fixture to test it

#

thanks for checking to see if it did fix it! we should probably make this configurable but im unsure where I'd wanna put this..... @leaden temple what do you reckon

spark oriole
#

Nice find guys. Really impressive

minor bone
#

Hello! Just checking if there's been an update regarding this point? Happy to be the one submitting the PR on this, just need to know what the TS team thinks is the best approach to fix this (ie a configuration? and where?)

This is sadly devaluing the SEO benefits of SSR for us, so would like to look into it