Custom Widgets
Create reusable widgets by composing BetterTUI primitives.
Basic Widget
Section titled “Basic Widget”import { Box, Text, useInput } from '@bettertui/react';
interface CardProps { title: string; description: string;}
export function Card({ title, description }: CardProps) { return ( <Box borderStyle="round" flexDirection="column" padding={1} gap={1} > <Text bold color="cyan">{title}</Text> <Text dimColor>{description}</Text> </Box> );}Interactive Widget
Section titled “Interactive Widget”import { Box, Text, useInput, useFocus } from '@bettertui/react';
interface ToggleProps { label: string; checked: boolean; onChange: (checked: boolean) => void;}
export function Toggle({ label, checked, onChange }: ToggleProps) { const [isFocused, focusProps] = useFocus();
useInput((key) => { if (isFocused && (key === ' ' || key === 'enter')) { onChange(!checked); } }, isFocused);
return ( <Box {...focusProps} gap={1}> <Text color={checked ? 'green' : 'red'}> {checked ? '[x]' : '[ ]'} </Text> <Text>{label}</Text> </Box> );}Composite Widget
Section titled “Composite Widget”import { Box, Text, Button, Dialog } from '@bettertui/react';
interface ConfirmDialogProps { isOpen: boolean; title: string; message: string; onConfirm: () => void; onCancel: () => void;}
export function ConfirmDialog({ isOpen, title, message, onConfirm, onCancel,}: ConfirmDialogProps) { return ( <Dialog isOpen={isOpen} onClose={onCancel} title={title}> <Text>{message}</Text> <Box flexDirection="row" gap={1} marginTop={1}> <Button onPress={onCancel}>Cancel</Button> <Button onPress={onConfirm} color="red">Confirm</Button> </Box> </Dialog> );}Best Practices
Section titled “Best Practices”- Keep widgets focused — One responsibility per widget
- Use composition — Build complex widgets from primitives
- Type everything — Full TypeScript support
- Handle focus — Make interactive widgets focusable
- Support accessibility — ARIA labels, keyboard navigation