QwikDev / qwik

Instant-loading web apps, without effort
https://qwik.dev
MIT License
20.84k stars 1.31k forks source link

[šŸž] Document head/title set from inside a component does not work in SSR #4712

Open Kasheftin opened 1 year ago

Kasheftin commented 1 year ago

Which component is affected?

Qwik City (routing)

Describe the bug

I made quite significant progress in developing a big app with custom dynamic routing and then met an obstacle with setting document head/title/og-meta. Now I'm stuck and not sure what is better - write a custom qwik-city following https://blog.brecht.io/why-and-how-i-created-a-spa-router-for-qwik/ or dive into qwik-city source code.

I created a separate repo demonstrating the problem, https://github.com/Kasheftin/qwik-ssr-head-issue.

The logic, described above, does not work in SSR (when the page is being initially loaded). It starts working during SPA navigation. I've read https://www.builder.io/blog/streaming-is-it-worth-it but did not find anything about separate head/body streaming in the qwik-city source code. I guess there's some special logic handling useDocumentHead and head exports and I'm looking for a way to turn it off. The real app routing is much more complex, and it's going to be very hard to reimplement it using directory-based routing and head export. The entire document including both head and body should be reactive.

Reproduction

https://github.com/Kasheftin/qwik-ssr-head-issue

Steps to reproduce

System Info

System:
    OS: Linux 5.19 Ubuntu 22.04.2 LTS 22.04.2 LTS (Jammy Jellyfish)
    CPU: (12) x64 Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
    Memory: 21.95 GB / 31.30 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 19.8.1 - ~/.nvm/versions/node/v19.8.1/bin/node
    npm: 9.5.1 - ~/.nvm/versions/node/v19.8.1/bin/npm
  Browsers:
    Chrome: 113.0.5672.126
  npmPackages:
    @builder.io/qwik: ^1.2.4 => 1.2.4 
    @builder.io/qwik-city: ^1.2.4 => 1.2.4 
    undici: 5.22.1 => 5.22.1 
    vite: 4.3.9 => 4.3.9

Additional Information

No response

manucorporat commented 1 year ago

Qwik does not do different streaming, for head, body. But qwik resolves the head data before start rendering.

Same technique is used by Remix or new Nextjs: https://remix.run/docs/en/main/route/meta https://nextjs.org/docs/app/building-your-application/optimizing/metadata

The <head> can not be changed later during SSR, by upcoming components, because it's already rendered, it's always sent in the network before your useTask$() even runs. That's why it's streaming.

Without this constrain, we could not start sending a single byte of HTML, because at any point any component could change the header.

The way it works in Qwik is by providing a declarative way to resolve the head information, even before rendering: https://qwik.builder.io/docs/pages/#head-export

Unfortunately, I dont think it's solvable without resolving the head title and stuff at the root level.

genox commented 11 months ago

I ended up in a similar position as OP. Being unable to change meta tags / head content from within a component would allow for more flexible data fetching. I have more than one dynamic catchall route so I could "fan" out the routeLoaders without having one giant data fetching at the root route but, let's say you have a list view route with filters, categories, etc - that instantly makes it necessary to handle the entire state logic in the route component and needing to push props down into components to render, including pagination methods etc or setting up even more global context.

It is doable, I already refactored most of it, but it is one of those things that I wish I was nudged too earlier when reading the docs. :)