sveltejs / kit

web development, streamlined
https://svelte.dev/docs/kit
MIT License
18.75k stars 1.95k forks source link

Dynamic SSR per request #12941

Open Celestialme opened 2 weeks ago

Celestialme commented 2 weeks ago

Describe the problem

before i could enable/disable SSR per request from hooks, now its not possible. I don't really need SSR for my app but for SEO its better to be enabled.

Describe the proposed solution

can we override export const ssr = false/true from hooks as it was before? so if I detect bot crawling I will return SSR true from hooks.

export function handle({ event, resolve }) {

let ssr = false;
if(bot){
   ssr=true
}

return resolve(event, { ssr });

Alternatives considered

No response

Importance

nice to have

Additional Information

No response

notramo commented 2 weeks ago

I don't see any benefits of it. Why not enable SSR always?

Celestialme commented 2 weeks ago

because it flickers on hydration and is slow for me on low end server.

Bishwas-py commented 2 weeks ago

This could be a basic logic for this. But I am not exactly sure if SSR is handled by preload func or not.

const handleSSR: Handle = async ({ event, resolve }) => {
  const googleUserAgent = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)';
  return resolve(event, {
    preload(input: { type: 'font' | 'css' | 'js' | 'asset'; path: string }): boolean {
      return event.request.headers.get('user-agent') !== googleUserAgent; // basic similar logic
    },
  });
};

export const handle = sequence(handleSSR);

Or, you can try create two separate servers, and using a middleman to where render the server with only SSR or not.

dominikg commented 2 weeks ago

just in case someone comes by here. google and potentially others are going to punish you if you present bots with a different experience than your regular users. Not sure if sending bots an ssr response and users not counts as that if the client rendering ultimately yields a similar experience, but don't come crying if you find your site downranked for playing dirty

Celestialme commented 2 weeks ago

just in case someone comes by here. google and potentially others are going to punish you if you present bots with a different experience than your regular users. Not sure if sending bots an ssr response and users not counts as that if the client rendering ultimately yields a similar experience, but don't come crying if you find your site downranked for playing dirty

No it is called dynamic rendering and google is totally fine with that, I am doing this about 6 years with previous svelte version and I am on first place. also you assume that SvelteKit SSR and CSR content is different and it does not work well enough? then it should be very noticeable for the eye when SSR to CSR happens in the browser. You are assuming this is cloaking which it is not. Cloaking is when you give bots different content which is not the case here.

for more to read https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering

yes it is says its not necessary now but not forbidden, but from my experience it is still superior to any rendering method.

Celestialme commented 2 weeks ago

This could be a basic logic for this. But I am not exactly sure if SSR is handled by preload func or not.

const handleSSR: Handle = async ({ event, resolve }) => {
  const googleUserAgent = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)';
  return resolve(event, {
    preload(input: { type: 'font' | 'css' | 'js' | 'asset'; path: string }): boolean {
      return event.request.headers.get('user-agent') !== googleUserAgent; // basic similar logic
    },
  });
};

export const handle = sequence(handleSSR);

Or, you can try create two separate servers, and using a middleman to where render the server with only SSR or not.

yes creating separate build is totally possible but very inconvenient. Another approach is to create routes under SSR and CSR and use hooks reroute. but it is also very inconvenient.

notramo commented 2 weeks ago

Could you provide an example of how it flickers on hydration? Is it a theme change? Or the page reloads?

Celestialme commented 2 weeks ago

https://github.com/user-attachments/assets/3547785a-7c05-49dc-9bb9-c17afa581cc8

https://github.com/user-attachments/assets/dcf0d4a5-41e5-4405-8610-b897f565a959

on dev server you can see it flashes first flickers after. on build version not as bad just flashing. without ssr it reloads instantly

notramo commented 2 weeks ago

This is not the expected outcome of an SSR. Svelte 5 has massively improved the hydration experience. Both the performance and features. My apps hydrate flawlessly and its UX is excellent.

I watched the video on 0.05× speed, and it became apparent that the SSR version only contains the header with the menu, but not the page main content. It's probably an issue with how your page is implemented. The problem is that search engines will see the same empty page (only the header menu). Try disabling JS in your browser and reloading the page. That's what search engines see from your page. Tweak the page until the no-JS version shows the same content as the JS version. Obviously it won't be interactive, but since your page is static text and images, that content is achievable with noscript too.

Also, do you use something like import { browser } from '$app/environment'?

notramo commented 2 weeks ago

I found out you have a leaderpack repo. I opened https://github.com/Celestialme/leaderpack/issues/1 to debug it there and only continue here if it's actually a SvelteKit bug.

Celestialme commented 2 weeks ago

This is not the expected outcome of an SSR. Svelte 5 has massively improved the hydration experience. Both the performance and features. My apps hydrate flawlessly and its UX is excellent.

I watched the video on 0.05× speed, and it became apparent that the SSR version only contains the header with the menu, but not the page main content. It's probably an issue with how your page is implemented. The problem is that search engines will see the same empty page (only the header menu). Try disabling JS in your browser and reloading the page. That's what search engines see from your page. Tweak the page until the no-JS version shows the same content as the JS version. Obviously it won't be interactive, but since your page is static text and images, that content is achievable with noscript too.

Also, do you use something like import { browser } from '$app/environment'?

thank you for looking into it. disabling javascript also renders full page. of course it is not interactable. that means page is rendered correctly on server side.

notramo commented 2 weeks ago

This feature is not needed in SvelteKit. It's a hydration issue, not a SvelteKit one. If hydration flickers, then the solution is not to disable it in SvelteKit, but to fix Svelte to avoid flickering.

Celestialme commented 2 weeks ago

I found that in root +layout.svelte

`{#if browser || SSR}

{/if} ` I can get SSR variable from load. now it does not flicker anymore and is way faster. but probably still nice to have more control from hooks side.

also found render() function from 'svelte/server' which renders on server but if i have $page used somewhere it breaks. also if i render page it does not take layout.svelte files into account so tailwind will not work as they are imported here.