Components
Silvery provides layout, text, input, and display components for building terminal UIs.
Box
The primary layout component. Uses CSS flexbox for layout.
import { Box, Text } from "silvery"
function Layout() {
return (
<Box flexDirection="row" justifyContent="space-between">
<Text>Left</Text>
<Text>Right</Text>
</Box>
)
}Scrolling
Use overflow="scroll" with scrollTo for automatic scrolling:
<Box flexDirection="column" overflow="scroll" scrollTo={selectedIndex}>
{items.map((item, i) => (
<Text key={i} inverse={i === selectedIndex}>
{item}
</Text>
))}
</Box>See Scrolling Guide for details.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
flexDirection | "row" | "column" | "row-reverse" | "column-reverse" | "row" | Main axis direction |
flexGrow | number | 0 | Grow factor |
flexShrink | number | 1 | Shrink factor |
flexBasis | number | string | - | Initial size |
justifyContent | "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "flex-start" | Main axis alignment |
alignItems | "flex-start" | "flex-end" | "center" | "stretch" | "stretch" | Cross axis alignment |
padding | number | 0 | Padding on all sides |
paddingX | number | 0 | Horizontal padding |
paddingY | number | 0 | Vertical padding |
margin | number | 0 | Margin on all sides |
width | number | string | - | Fixed or percentage width |
height | number | string | - | Fixed or percentage height |
minWidth | number | - | Minimum width |
minHeight | number | - | Minimum height |
borderStyle | "single" | "double" | "round" | "bold" | "classic" | - | Border style |
borderColor | string | - | Border color |
overflow | "visible" | "hidden" | "scroll" | "visible" | Overflow behavior |
scrollTo | number | - | Child index to keep visible |
Text
Renders text with styling. Supports Chalk strings.
import { Text } from "silvery"
import chalk from "chalk"
// Basic styling
<Text color="green" bold>Success!</Text>
// Chalk strings work too
<Text>{chalk.red.bold("Error!")}</Text>Auto-Truncation
Text automatically truncates to fit available width:
<Box width={20}>
<Text>This is a very long text that will be truncated</Text>
</Box>
// Output: "This is a very lon…"Opt out with wrap={false} if you need overflow behavior.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
color | string | - | Text color |
backgroundColor | string | - | Background color |
bold | boolean | false | Bold text |
italic | boolean | false | Italic text |
underline | boolean | false | Underlined text |
strikethrough | boolean | false | Strikethrough text |
dimColor | boolean | false | Dimmed color |
inverse | boolean | false | Swap foreground/background |
wrap | "wrap" | "truncate" | "truncate-start" | "truncate-middle" | "truncate-end" | "truncate" | Text wrapping behavior |
Newline
Renders a newline character.
import { Newline, Text } from "silvery"
<Text>Line 1</Text>
<Newline />
<Text>Line 2</Text>Spacer
Flexible space that expands to fill available room.
import { Box, Spacer, Text } from "silvery"
function Layout() {
return (
<Box>
<Text>Left</Text>
<Spacer />
<Text>Right</Text>
</Box>
)
}Static
Renders content that won't be updated. Useful for logs or output that scrolls up.
import { Static, Box, Text } from "silvery"
function App() {
const [logs, setLogs] = useState<string[]>([])
return (
<Box flexDirection="column">
<Static items={logs}>{(log, i) => <Text key={i}>{log}</Text>}</Static>
<Text>Current status...</Text>
</Box>
)
}Props
| Prop | Type | Description |
|---|---|---|
items | T[] | Array of items to render |
children | (item: T, index: number) => ReactNode | Render function |
Input Components
TextInput
Single-line text input with full readline shortcuts (Ctrl+A/E, Ctrl+K/U, Alt+B/F, Ctrl+Y with kill ring).
import { TextInput } from "silvery"
function MyInput() {
return <TextInput value={text} onChange={setText} onSubmit={handleSubmit} placeholder="Type here..." prompt="> " />
}TextArea
Multi-line text editing with cursor navigation, line wrapping, and text selection.
import { TextArea } from "silvery"
function MyEditor() {
return (
<TextArea
value={text}
onChange={setText}
height={5}
placeholder="Type here..."
submitKey="ctrl+enter"
onSubmit={handleSubmit}
scrollMargin={1}
/>
)
}| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Current value (controlled) |
defaultValue | string | - | Initial value (uncontrolled) |
onChange | (value: string) => void | - | Called when value changes |
onSubmit | (value: string) => void | - | Called on submit key press |
submitKey | "ctrl+enter" | "enter" | "meta+enter" | "ctrl+enter" | Key combo to trigger submit |
height | number | required | Visible height in rows |
placeholder | string | - | Placeholder text when empty |
isActive | boolean | - | Override focus system |
cursorStyle | "block" | "underline" | "block" | Unfocused cursor style |
scrollMargin | number | 1 | Context lines above/below cursor when scrolling |
disabled | boolean | false | Ignore input and dim text |
maxLength | number | - | Maximum character count |
testID | string | - | Test ID for focus system |
Features: Shift+Arrow selection, Ctrl+A select all, Ctrl+Home/End document navigation, word-wise movement (Ctrl+Arrow), readline shortcuts (Ctrl+K/U/Y), column memory for vertical movement.
SelectList
Single-select list with keyboard navigation (arrow keys, j/k, Home/End), disabled item support, and maxVisible for scroll windowing.
import { SelectList } from "silvery"
function MyList() {
return (
<SelectList
items={[
{ label: "React", value: "react" },
{ label: "Vue", value: "vue" },
{ label: "Svelte", value: "svelte" },
]}
onSelect={(item) => console.log(item.value)}
/>
)
}Toggle, Button
Simple interactive primitives for boolean toggles and clickable buttons.
Display Components
| Component | Description |
|---|---|
Spinner | Animated spinner with presets (dots, line, arc, bounce) |
ProgressBar | Determinate and indeterminate progress with custom fill |
Table | Column-aligned table with header, per-column alignment |
Badge | Styled label/tag |
Divider | Horizontal rule |
VirtualList | O(1) scroll for thousands of items — see List Components |
VirtualView | Virtualized arbitrary content |
Console | Captures console.log output via patchConsole() |
Transform | Per-line string transformation on children |
Image | Kitty graphics / Sixel with text fallback |
Link | OSC 8 hyperlink |
Shadcn-Style Components
Higher-level pre-styled components using $token semantic colors. Import from silvery:
| Component | Description |
|---|---|
Form / FormField | Form layout with label, description, error message |
Toast / useToast() | Auto-dismiss notifications with severity levels |
CommandPalette | Fuzzy-search command palette (Ctrl+K pattern) |
TreeView | Expandable/collapsible tree with keyboard navigation |
Breadcrumb | Path breadcrumb with separator customization |
Tabs / TabList / Tab / TabPanel | Tabbed interface with keyboard navigation |
Tooltip | Contextual tooltip overlay |
Skeleton | Loading placeholder with configurable width/lines |
ErrorBoundary | React error boundary with resetKeys and fallback |
ModalDialog | Modal overlay with focus trapping |
PickerDialog / PickerList | Selection dialog |
These components use the theming system — wrap your app in ThemeProvider with defaultDarkTheme or defaultLightTheme and the components will use semantic colors automatically. Any color prop starting with $ is resolved against the active theme (e.g. color="$primary", backgroundColor="$surface-bg").
@silvery/ui — Progress & Input Package
@silvery/ui is a separate package with progress indicators and ergonomic async wrappers. It works both as standalone CLI output (no React) and as React components inside Silvery apps.
npm install @silvery/uibun add @silvery/uipnpm add @silvery/uiyarn add @silvery/uiCLI mode (direct stdout, no React):
import { Spinner, ProgressBar, MultiProgress } from "@silvery/ui/cli"
const stop = Spinner.start("Loading...")
await doWork()
stop()Wrappers (ergonomic async adapters):
import { withSpinner, withProgress } from "@silvery/ui/wrappers"
const data = await withSpinner(fetchData(), "Loading data...")Declarative steps:
import { steps } from "@silvery/ui/progress"
const loader = steps({ loadModules, parseConfig, validate })
await loader.run({ clear: true })React components (for Silvery/Ink apps):
import { Spinner, ProgressBar, Tasks, Task } from "@silvery/ui/react"
import { TextInput, Select } from "@silvery/ui/input"See the @silvery/ui README for full documentation.