Variable: ScrollList
constScrollList:ForwardRefExoticComponent<ScrollListProps&RefAttributes<ScrollListRef>>
A scrollable list with externally controlled selection.
Remarks
This component extends ScrollView from ink-scroll-view to provide:
- Externally controlled selection: Selection state is managed by the parent via
selectedIndexprop - Automatic scroll-into-view: When
selectedIndexchanges, the component scrolls to ensure visibility - Configurable alignment: Control how selected items are positioned within the viewport
- Responsive to layout changes: Maintains selected item visibility when viewport or content changes
Design Philosophy
ScrollList follows the “controlled component” pattern where the parent component owns all state. This provides several benefits:
- Predictable behavior: The parent always knows the current selection
- Easy integration: Works seamlessly with state management libraries
- Flexible input handling: Parent decides how keyboard/mouse events affect selection
- Testable: Selection logic lives in the parent and is easy to unit test
Automatic Scroll Behavior
The component automatically scrolls to keep the selected item visible in these scenarios:
- When
selectedIndexprop changes - When viewport size changes (e.g., terminal resize)
- When content height changes (e.g., items added/removed)
- When an item’s height changes and it affects the selected item’s position
Important Caveats
- No input handling: This component does NOT handle keyboard input.
Use
useInputfrom Ink to updateselectedIndexin the parent. - No resize detection: Does NOT automatically respond to terminal resize.
Listen to
process.stdout’sresizeevent and callremeasure()on the ref. - Parent manages bounds: The component does NOT clamp
selectedIndex. The parent should ensure the value is within valid range [0, itemCount - 1].
Examples
Basic Usage with Keyboard Navigation
import React, { useRef, useState } from "react";
import { Box, Text, useInput } from "ink";
import { ScrollList, ScrollListRef } from "ink-scroll-list";
const Demo = () => {
const listRef = useRef<ScrollListRef>(null);
const [selectedIndex, setSelectedIndex] = useState(0);
const items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
// Handle keyboard navigation
useInput((input, key) => {
if (key.downArrow) {
setSelectedIndex((prev) => Math.min(prev + 1, items.length - 1));
}
if (key.upArrow) {
setSelectedIndex((prev) => Math.max(prev - 1, 0));
}
if (input === "g") {
setSelectedIndex(0); // Go to first
}
if (input === "G") {
setSelectedIndex(items.length - 1); // Go to last
}
});
return (
<ScrollList ref={listRef} height={5} selectedIndex={selectedIndex}>
{items.map((item, i) => (
<Box key={i}>
<Text color={i === selectedIndex ? "blue" : "white"}>
{i === selectedIndex ? "> " : " "}
{item}
</Text>
</Box>
))}
</ScrollList>
);
};With Different Alignment Modes
// Center alignment - great for search results or spotlight features
<ScrollList
height={10}
selectedIndex={searchResultIndex}
scrollAlignment="center"
>
{results.map((result, i) => (
<SearchResult key={i} result={result} highlighted={i === searchResultIndex} />
))}
</ScrollList>
// Top alignment - always shows selected item at top
<ScrollList
height={5}
selectedIndex={selectedIndex}
scrollAlignment="top"
>
{items.map((item, i) => <Item key={i} {...item} />)}
</ScrollList>