data-table
Build virtualized data tables with @virtuoso.dev/data-table. Use this skill when (1) building…
Build chat, messaging, and AI conversation UIs with @virtuoso.dev/message-list. Use this skill when (1) building a chat or messaging interface, (2) streaming AI assistant responses into a conversation, (3) loading older messages when scrolling up, (4) adding scroll-to-bottom
$ npx -y skills add petyosi/react-virtuoso --skill message-list --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/message-listContext preview
The summary Claude sees to decide when to auto-load this skill.
Build chat, messaging, and AI conversation UIs with @virtuoso.dev/message-list. Use this skill when (1) building a chat or messaging interface, (2) streaming AI assistant responses into a conversation, (3) loading older messages when scrolling up, (4) adding scroll-to-bottom
name: message-list description: >- Build chat, messaging, and AI conversation UIs with @virtuoso.dev/message-list. Use this skill when (1) building a chat or messaging interface, (2) streaming AI assistant responses into a conversation, (3) loading older messages when scrolling up, (4) adding scroll-to-bottom buttons or unseen-message indicators, (5) switching between channels/conversations, or (6) any task involving VirtuosoMessageList, VirtuosoMessageListLicense, useVirtuosoMethods, useVirtuosoLocation, scrollModifier, notifyItemsChanged, scrollToBottomIfAtBottom, scrollToBottomAlways, smooth-scroll retargeting, replacement anchors, or data.append/prepend/map/replace. The package is commercial and requires a license key.
`VirtuosoMessageList` is a virtualized list purpose-built for human and AI chat: stick-to-bottom behavior, streaming responses that grow without scroll jumps, history prepending that preserves the visual position, and scroll position tracking. Use it instead of plain `Virtuoso` when building conversation UIs — these behaviors are built in rather than hand-assembled.
The package is commercial (annual, per-developer). Every instance must be wrapped in `VirtuosoMessageListLicense`:
<VirtuosoMessageListLicense licenseKey={licenseKey}>
<VirtuosoMessageList ... />
</VirtuosoMessageListLicense>An empty `licenseKey=""` is licensed for 30-day non-production evaluation: it keeps rendering in development with a console reminder, while production without a valid key renders an error. Validation is local — no network requests. Surface this to the user when introducing the package into a project; keys come from <https://virtuoso.dev/pricing/>. Prefer an app-level wrapper that reads from the normal public client-side environment configuration.
Instead of separate imperative scroll calls, each data update includes a `scrollModifier` describing how the viewport should react:
const [data, setData] = useState<VirtuosoMessageListProps<Message, null>['data']>(() => ({
data: initialMessages,
scrollModifier: { type: 'item-location', location: { index: 'LAST', align: 'end' } },
}))
<VirtuosoMessageList<Message, null> style={{ height: '100%' }} data={data} computeItemKey={({ data }) => data.key} ItemContent={ItemContent} />`ItemContent` is a component receiving `{ data, index, context }` props (not positional arguments like react-virtuoso). Extract it outside the parent render function unless it needs to close over local state.
External row state is different from data updates. If `context`, side maps, expansion state, reactions, approvals, or loading flags can change row height without changing the data array, call `notifyItemsChanged({ scrollToBottom })` on the message-list ref after updating that state.
| Modifier | When to use | | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | | `{ type: 'item-location', location, purgeItemSizes? }` | Initial load or channel switch; `purgeItemSizes: true` clears cached sizes when the items are different | | `{ type: 'auto-scroll-to-bottom', autoScroll }` | New messages are appended; callback can return `false`, a behavior, `true`, or an item location | | `'prepend'` | Older messages added to the top; keeps the current messages visually in place | | `{ type: 'items-change', behavior }` | Existing data items changed size, such as streaming text or reactions stored in the data item | | `'remove-from-start'` / `'remove-from-end'` | Trimming data while preserving the visual position | | `null` / `undefined` | Leave the scroll position alone |
In callback form, returning a behavior or item location scrolls even when `atBottom` is false. Return `false` to preserve the viewport for a scrolled-up user.
import { scrollToBottomIfAtBottom } from '@virtuoso.dev/message-list'
setData((current) => ({
data: [...current.data, incoming],
scrollModifier: {
type: 'auto-scroll-to-bottom',
autoScroll: scrollToBottomIfAtBottom,
},
}))Use `scrollToBottomIfAtBottom` for remote messages. It returns `'smooth'` when the list is already at the bottom or already scrolling there, otherwise `false`. If the app needs an unseen-message counter, use the inline callback form and increment the counter before returning `false`.
import { scrollToBottomAlways } from '@virtuoso.dev/message-list'
setData((current) => ({
data: [...current.data, optimisticLocalMessage],
scrollModifier: {
type: 'auto-scroll-to-bottom',
autoScroll: scrollToBottomAlways,
},
}))Use `scrollToBottomAlways` for local current-user actions where the new last item should become visible even if the user was scrolled up. If a realtime socket echoes the same optimistic message back, reconcile it by `localId` or another client-generated identity and treat it as confirmation, not a new incoming remote message.
Append an empty assistant message, then grow it as tokens arrive:
setData((current) => ({
data: current.data.map((msg) => (msg.key === botKey ? { ...msg, text: msg.text + chunk } : msg)),
scrollModifier: { type: 'items-change', behavior: 'smooth' },
}))`items-change` keeps the view pinn
The most complete React virtualization rendering family of components. Variable sized items out of the box; no manual measurements or hard-coding item heights is necessary; Chat message list UI; Grouped mode with sticky headers; Responsive grid layout;
Repo: petyosi/react-virtuoso
Build virtualized data tables with @virtuoso.dev/data-table. Use this skill when (1) building…
Build virtualized lists, grids, and tables with react-virtuoso. Use this skill when (1)…
Manage application state with the @virtuoso.dev/reactive-engine-* package family. Use this…