Skip to content

Question/bug: Are nested SpatialNavigationVirtualizedList (vertical parent + horizontal rails) supported? scrollBehavior breaks and virtualization collapses at scale #214

@nriccar

Description

@nriccar

Describe the bug
When nesting SpatialNavigationVirtualizedList (SNVL) — a vertical parent with horizontal SNVL rails as children — the horizontal scrollBehavior='stick-to-start' becomes inconsistent (doesn’t snap/stick reliably), and performance degrades as the screen adds more rails/items (focus lag after a few moves, virtualization seems less effective).

To Reproduce

import React, { memo, useMemo } from 'react'
import { Text, View } from 'react-native'
import {
  DefaultFocus,
  SpatialNavigationFocusableView,
  SpatialNavigationNode,
  SpatialNavigationRoot,
  SpatialNavigationVirtualizedList,
} from 'react-tv-space-navigation'

const rails = new Array(4).fill(0).map((_, r) => ({
  id: `rail-${r}`,
  items: new Array(10).fill(0).map((__, i) => ({ id: `r${r}-i${i}` })),
}))

const ROW_HEIGHT = 220
const TILE_W = 120
const GAP = 16
const ITEM_SIZE = TILE_W + GAP

export const Test = () => {
  const data = useMemo(() => rails, [])

  return (
    <View
      style={{
        flex: 1,
        overflow: 'hidden',
      }}>
      <SpatialNavigationRoot>
        <SpatialNavigationVirtualizedList
          orientation='vertical'
          data={data}
          itemSize={ROW_HEIGHT}
          additionalItemsRendered={1}
          scrollBehavior='stick-to-start'
          renderItem={({ item: rail }) => (
            // CHILD: horizontal SNVL per row
            <SpatialNavigationVirtualizedList
              orientation='horizontal'
              data={rail.items}
              itemSize={ITEM_SIZE}
              scrollBehavior='stick-to-start' // ← doesn't work
              additionalItemsRendered={1}
              renderItem={({ item }) => (
                <SpatialNavigationNode>
                  <Tile id={item.id} width={TILE_W} height={ROW_HEIGHT - GAP} />
                </SpatialNavigationNode>
              )}
            />
          )}
        />
      </SpatialNavigationRoot>
    </View>
  )
}

const Tile = memo(
  ({ id, width, height }: { id: string; width: number; height: number }) => {
    const Wrapper = id === 'rail-0' ? DefaultFocus : View
    return (
      <Wrapper>
        <SpatialNavigationFocusableView>
          {({ isFocused }) => (
            <View
              style={{
                width,
                height,
                backgroundColor: isFocused ? 'red' : 'blue',
                justifyContent: 'center',
                alignItems: 'center',
              }}>
              <Text
                style={{
                  color: 'white',
                  fontSize: 20,
                  fontWeight: 'bold',
                }}>
                {id}
              </Text>
            </View>
          )}
        </SpatialNavigationFocusableView>
      </Wrapper>
    )
  },
)

Observed behavior

Holding RIGHT/LEFT within a horizontal rail: after a few items, snapping to “start” doesn't work.

As more rails/items are added, overall responsiveness drops and focus moves become laggy (virtualization seems to keep more mounted than expected) - testing in release modes.

Expected behavior

  • With vertical SNVL parent + horizontal SNVL children, scrollBehavior='stick-to-start' should remain reliable in each horizontal rail.
  • LEFT/RIGHT within a rail and UP/DOWN between rails should be smooth.
  • Virtualization should remain effective (only a small window mounted) as rails/items scale.

Library version: 5.2.0
React Native version: 0.77.0

Additional context

I followed the Pitfalls & Troubleshooting doc: every item is always wrapped in a SpatialNavigationNode / SpatialNavigationFocusableView (no conditional mounting); only inner content changes.

This issue appears specifically when the vertical container is SNVL; with a vertical SpatialNavigationScrollView, snapping is correct but performance doesn’t scale since everything stays mounted.

Questions:

  • Is SNVL→SNVL nesting (vertical parent + horizontal children) officially supported?
  • If yes, are there recommended props/patterns to keep scrollBehavior and virtualization stable at scale?
  • If not recommended, what’s the preferred pattern for large datasets?

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions