React Hooks
Keyboard shortcuts, renderer context and terminal dimensions.
Hooks
The React bindings expose hooks analogous to the Solid ones. Use useKeyboard to register a key handler, useRenderer to access the current renderer and useTerminalDimensions to react to resize events.
| Hook | Signature | Description |
|---|---|---|
useRenderer | () => CliRenderer | Returns the active renderer from context. Throws if used outside the Cascade React root. |
useKeyboard | (handler: (e: KeyEvent) => void, options?: { release?: boolean }) => void | Subscribes to keyboard events. By default receives press events (including repeats). Use release: true to also receive release events. |
useTerminalDimensions | () => { width: number; height: number } | Returns terminal width/height and re-renders on resize. |
useTimeline | (options?: TimelineOptions) => Timeline | Creates a timeline and registers it with the animation engine; automatically pauses/unregisters on unmount. |
import { useKeyboard } from "@cascadetui/react"
function App() {
useKeyboard((event) => {
if (event.name === "q") process.exit(0)
})
return <text content="Press q to quit" />
}Combine these hooks with standard React state and effect patterns to build interactive applications.
import { useTerminalDimensions } from "@cascadetui/react"
function App() {
const dims = useTerminalDimensions()
return <text content={"Size: " + dims.width + "x" + dims.height} />
}Recipe: track pressed keys (press + release)
import { useKeyboard } from "@cascadetui/react"
import { useMemo, useState } from "react"
function App() {
const [pressed, setPressed] = useState<Set<string>>(() => new Set())
useKeyboard(
(e) => {
setPressed((prev) => {
const next = new Set(prev)
if (e.eventType === "release") next.delete(e.name)
else next.add(e.name)
return next
})
},
{ release: true },
)
const text = useMemo(() => Array.from(pressed).sort().join(", ") || "(none)", [pressed])
return <text>{"Pressed: " + text}</text>
}For animation, use useTimeline to register a timeline with the engine. A simple pattern is to update local state in onUpdate and let React re-render declaratively.
import { useTimeline } from "@cascadetui/react"
import { useEffect, useState } from "react"
function App() {
const [progress, setProgress] = useState(0)
const timeline = useTimeline({ duration: 1200, loop: true })
useEffect(() => {
timeline.add(
{ t: 0 },
{
t: 1,
duration: 1200,
ease: "linear",
onUpdate: (a) => setProgress(a.targets[0].t),
},
)
}, [])
return (
<box border padding={1} width={40}>
<text>{"Loading: " + Math.round(progress * 100) + "%"}</text>
<box style={{ width: Math.max(1, Math.round(progress * 36)), height: 1, backgroundColor: "#7aa2f7" }} />
</box>
)
}