Scroll to Group
This example re-creates the UI of the iOS contacts listview.
import {GroupedVirtuoso} from 'react-virtuoso'
import { useMemo, useRef } from 'react'
export default function App() {
const { users, groups, groupCounts } = useMemo(() => {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
const users = letters.flatMap((letter) => {
return Array.from({ length: 20 }, (_, index) => ({
name: `${letter} User ${index}`,
initials: `${letter}${index}`,
description: `Description for user ${index}`,
}))
})
const groups = letters.slice(0, 15)
const groupCounts = letters.map((letter, index) => {
return users.filter((user, userIndex) => user.name.startsWith(letter)).length
})
return { users, groups, groupCounts }
}, [])
const virtuoso = useRef(null)
return (
<div style={{ display: 'flex', height: '100%' }}>
<div style={{ flex: 1 }}>
<GroupedVirtuoso
ref={virtuoso}
groupCounts={groupCounts}
groupContent={index => {
return <div style={{
backgroundColor: 'var(--background)',
padding: '0.3rem 1rem'
}}>{groups[index]}</div>
}}
itemContent={index => {
return <div style={{ padding: '0.5rem 1rem' }}>
<h4>{users[index].name}</h4>
<p style={{ marginBottom: 0 }}>{users[index].description}</p>
</div>
}}
/>
</div>
<ul
style={{
marginLeft: '0.5rem',
paddingLeft: '0',
listStyle: 'none',
fontSize: '0.8rem',
}}
>
{groupCounts
.reduce(
({ firstItemsIndexes, offset }, count) => {
return {
firstItemsIndexes: [...firstItemsIndexes, offset],
offset: offset + count,
}
},
{ firstItemsIndexes: [], offset: 0 }
)
.firstItemsIndexes.map((itemIndex, index) => (
<li key={index}>
<a
href="#"
style={{color: 'var(--foreground)', padding: '0.5rem'}}
onClick={e => {
e.preventDefault()
virtuoso.current.scrollToIndex({
index: itemIndex,
})
}}
>
{groups[index]}
</a>
</li>
))}
</ul>
</div>
)
}