inokawa / virtua

A zero-config, fast and small (~3kB) virtual list (and grid) component for React, Vue, Solid and Svelte.
https://inokawa.github.io/virtua/
MIT License
1.3k stars 44 forks source link

Next.js WVList SSR #171

Closed lithdew closed 10 months ago

lithdew commented 1 year ago

Describe the bug When a WVList is SSR’d on NextJS, the initial HTML markup has all row items rendered as hidden.

InitialItemsCount, and overscan are provided to WVList.

To Reproduce Will provide a reproduction in some time - writing this report on my phone.

Expected behavior All row items should be made visible when SSR rendered.

Platform:

Additional context The row items were flexbox elements that have a min-width of zero. Unsure if it is possible for this to be an issue though.

NextJS version is 13.4.19 (the latest).

inokawa commented 1 year ago

This lib hide items until items are measured because they can cause visual glitch on initial measuring with current implementation of WVList. And it's impossible to know actual sizes of items in server without layouting on browser (we can know if they are fixed sized items).

So could you tell me the problem in detail caused by hidden items? We may need escape hatch for it.

lithdew commented 1 year ago

This lib hide items until items are measured because they can cause visual glitch on initial measuring with current implementation of WVList. And it's impossible to know actual sizes of items in server without layouting on browser (we can know if they are fixed sized items).

So could you tell me the problem in detail caused by hidden items? We may need escape hatch for it.

:thinking: So for my use case, I am displaying an infinitely scrollable post feed for a social network-like website. I would like the initial list of items pre-rendered via. SSR to improve my site's SEO, and to avoid content layout shift.

Virtual list libraries such as virtuoso seem to handle pre-rendering the list of items just fine, so sizing somehow does happen on server-side (perhaps the initialItemCount number of items in the list are SSR'd and made visible first, and then sizing happens after? Or is this how the library works, or will this cause the visual glitches you mentioned?).

inokawa commented 1 year ago

react-virtuso will work without hidden style because it has different strategy from virtua.

As described in this gist, there are 2 major types of strategy of virtual scroller. react-virtuoso seems to be using relative positioning, on the other hand virtua, react-virtualized and react-window using absolute positioning. In absolute positioning every items should have correct top before initial display but not in relative positioning because we can just set position 0 to the spacer if scrolling starts from page top.

// absolute
<div style={{ position: 'relative' }}>
  <div style={{ position: 'absolute', top: 0 }}>0</div>
  <div style={{ position: 'absolute', top: 120 }}>1</div>
  <div style={{ position: 'absolute', top: 240 }}>2</div>
  ...
</div>

// relative
<div>
  <div style={{ paddingTop:0, paddingBottom: xxxx }}>
    <div>0</div>
    <div>1</div>
    <div>2</div>
    ...
  </div>
</div>

I need some investigation but I'm now thinking is to change strategy of WVList to relative from absolute. Why I adopted absolute strategy is that it was necessary for calculation in scrollToIndex of VList, but WVList doesn't have the method.

inokawa commented 1 year ago

As a workaround for fixed sized items, you can override default style of items with components.Item prop and provide initial item size in SSR with initialItemSize prop:

const OverridedItem= forwardRef<HTMLDivElement, CustomItemComponentProps>(
  ({ children, style }, ref) => {
    return (
      <div ref={ref} style={{ ...style, visibility: 'visible' }}>
        {children}
      </div>
    );
  }
);

<VList components={{ Item: OverridedItem }} initialItemCount={10} initialItemSize={100} >{elements}</VList>

I think it will only work for fixed sized items and dynamic sized items may cause layout shifts.

inokawa commented 10 months ago

Fixed in 0.19.0.