Custom Hook
custom_hook shows the most common business-side custom hook: a command palette’s query, filtered results, cursor clamping, and keyboard handling are all placed in use_command_palette, while the component only receives the returned snapshot to draw the UI.

This recording is generated by docs/tapes/custom-hook.tape and runs examples/advanced/custom_hook.rs. It types a filter, moves the cursor, submits a command, clears the filter, and submits another command.
Why start with compositional hooks
Section titled “Why start with compositional hooks”Most business custom hooks do not need to implement the low-level Hook trait. If the behavior can be expressed with built-in hooks, directly compose those hooks in a fixed order:
Although this function is just an ordinary Rust function, it is still a hook: every frame it calls use_state, use_memo, use_effect, and use_event_handler in a fixed order, then returns the data the component needs to render.
Hook call order
Section titled “Hook call order”Custom hooks must follow the same rule internally: hook calls cannot be placed inside if, for, match, or early-return branches. This is unsafe:
If the first frame has enabled=true and the next frame has enabled=false, the second use_state index shifts and the runtime may retrieve the wrong hook type.
Use use_memo for derived data
Section titled “Use use_memo for derived data”Filtered results are not independent state. They are derived from query and commands:
use_memo compares dependencies with PartialEq. Here the dependency is a String: when the query is unchanged, it reuses the previous Vec<usize>; when the query changes, it filters again.
Use use_effect to correct state
Section titled “Use use_effect to correct state”When filtered results shrink, the old cursor can be out of bounds. That correction is a side effect determined by visible_len and the current cursor:
This is more reliable than manually correcting the cursor in every key branch. State writes still go through the State handle, so they wake the next redraw.
Events can be wrapped too
Section titled “Events can be wrapped too”use_command_palette registers its own keyboard handler. Query, movement, and submit keys return Consumed; keys that do not belong to the command palette, such as q, return Ignored so the outer app can handle them.
This is the same model as input exclusivity: a hook can register handlers, but it must still be explicit about returning Consumed or Ignored.
When to implement the Hook trait
Section titled “When to implement the Hook trait”Implement the low-level Hook trait only when the custom capability needs its own lifecycle callbacks, for example:
- It needs to poll an external future or manual waker in
poll_change. - It needs to unsubscribe resources in
on_drop. - It needs to read framework context in
pre_component_update/post_component_update.
For ordinary business state, derived data, form input, selection models, and event handling, prefer compositional hooks. They are easier to keep in stable call order and easier to validate in examples and docs.
Next, see the Hooks core model, or continue to Native Widget Bridging to learn how the draw stage connects to ratatui widgets.