# Forwarding Keyboard Inputs

The List widget supports receiving keyboard events from other widgets through the `pipeKeyDown` prop. A common use case is a search interface where the user types in a TextField while navigating results with arrow keys.

```tsx
import { createModel } from "cx/data";
import { Controller, KeySelection } from "cx/ui";
import { HighlightedSearchText, List, TextField } from "cx/widgets";
import { getSearchQueryPredicate, KeyCode } from "cx/util";

interface Item {
  id: number;
  text: string;
}

interface Model {
  query: string;
  records: Item[];
  filteredRecords: Item[];
  selection: number;
  $record: Item;
}

const m = createModel<Model>();

const countries = [
  "Australia",
  "Austria",
  "Belgium",
  "Brazil",
  "Canada",
  "Denmark",
  "Finland",
  "France",
  "Germany",
  "Italy",
  "Japan",
  "Netherlands",
  "Norway",
  "Spain",
  "Sweden",
  "Switzerland",
  "United Kingdom",
  "United States",
];

class PageController extends Controller {
  listKeyDownPipe?: ((e: React.KeyboardEvent) => void) | null;

  onInit() {
    this.store.set(
      m.records,
      countries.map((text, i) => ({ id: i + 1, text })),
    );

    this.addComputable(
      m.filteredRecords,
      [m.records, m.query],
      (records, query) => {
        if (!query) return records;
        let predicate = getSearchQueryPredicate(query);
        return records.filter((r) => predicate(r.text));
      },
    );
  }

  pipeKeyDown(cb: ((e: React.KeyboardEvent) => void) | null) {
    this.listKeyDownPipe = cb;
  }

  onKeyDown(e: React.KeyboardEvent) {
    if (
      e.keyCode === KeyCode.up ||
      e.keyCode === KeyCode.down ||
      e.keyCode === KeyCode.enter
    ) {
      this.listKeyDownPipe?.(e);
      e.preventDefault();
    }
  }
}

export default (
  <div
    controller={PageController}
    class="flex flex-col gap-2"
    style="width: 300px"
  >
    <TextField
      value={m.query}
      placeholder="Search..."
      onKeyDown={(e, instance) =>
        instance.getControllerByType(PageController).onKeyDown(e)
      }
      inputAttrs={{ autoComplete: "off" }}
      autoFocus
    />
    <List
      records={m.filteredRecords}
      selection={{ type: KeySelection, bind: m.selection, keyField: "id" }}
      mod="bordered"
      style="height: 200px; overflow-y: auto"
      emptyText="No items found."
      recordAlias={m.$record}
      pipeKeyDown={(cb, instance) =>
        instance.getControllerByType(PageController).pipeKeyDown(cb)
      }
      focused
      scrollSelectionIntoView
    >
      <HighlightedSearchText text={m.$record.text} query={m.query} />
    </List>
  </div>
);

```

## How It Works

1. The List's `pipeKeyDown` prop receives a callback for forwarding keyboard events
2. Store the callback in the controller using `getControllerByType` for type safety
3. On the TextField's `onKeyDown`, forward arrow and enter keys to the stored callback
4. Use `focused` prop to enable keyboard navigation without clicking the list
5. Use `scrollSelectionIntoView` to keep the selected item visible

## See Also

- [TextField](/docs/forms/text-field) - Text input field
- [List](/docs/forms/list) - Basic list configuration