{"id":14924,"library":"solid-list","title":"Solid List Utility","description":"solid-list is a SolidJS utility designed to simplify the creation of accessible and keyboard-navigable lists, suitable for components like search results, custom selects, or autocompletes. It supports both vertical and horizontal orientations, single and multi-selection patterns, and integrates with `rtl` and `ltr` text directions. The library is unopinionated about styling and works with various list implementations, including virtualized lists, by providing utilities rather than prescriptive components. Key features include optional looping behavior, a 'Vim mode' for navigation, and configurable tab key handling. It is currently at version 0.3.0 and is part of the `corvu` collection of unstyled UI primitives for SolidJS. This suggests active development and a release cadence aligned with other `corvu` packages, which generally see updates every few weeks or months. Its primary differentiator is its strong focus on accessibility and keyboard navigation capabilities without imposing any specific UI or styling opinions on the developer.","status":"active","version":"0.3.0","language":"javascript","source_language":"en","source_url":"https://github.com/corvudev/corvu","tags":["javascript","solid","solidjs","keyboard","accessible","list","typescript"],"install":[{"cmd":"npm install solid-list","lang":"bash","label":"npm"},{"cmd":"yarn add solid-list","lang":"bash","label":"yarn"},{"cmd":"pnpm add solid-list","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency, required for SolidJS reactivity and component model.","package":"solid-js","optional":false}],"imports":[{"note":"solid-list is an ESM-first package. Use ES module import syntax. `createList` is a SolidJS factory function.","wrong":"const createList = require('solid-list')","symbol":"createList","correct":"import { createList } from 'solid-list'"}],"quickstart":{"code":"import { createSignal, For } from 'solid-js';\nimport { createList } from 'solid-list';\n\nconst Search = () => {\n  const [results, setResults] = createSignal([\n    { id: '1', name: 'Apple', href: '/products/apple' },\n    { id: '2', name: 'Banana', href: '/products/banana' },\n    { id: '3', name: 'Cherry', href: '/products/cherry' },\n    { id: '4', name: 'Date', href: '/products/date' }\n  ]);\n\n  const { active, setActive, onKeyDown } = createList({\n    items: () => results().map(result => result.id), // required, should be a reactive accessor\n    initialActive: null, // default, T | null\n    orientation: 'vertical', // default, 'vertical' | 'horizontal'\n    loop: true, // default\n    textDirection: 'ltr', // default, 'ltr' | 'rtl'\n    handleTab: true, // default\n    vimMode: false, // default\n    onActiveChange: (newActiveId) => {\n      console.log('Active item changed to:', newActiveId);\n      // Optionally, scroll into view or navigate\n    }\n  });\n\n  const handleInput = (e) => {\n    const query = e.target.value.toLowerCase();\n    setResults([\n      { id: '1', name: 'Apple', href: '/products/apple' },\n      { id: '2', name: 'Banana', href: '/products/banana' },\n      { id: '3', name: 'Cherry', href: '/products/cherry' },\n      { id: '4', name: 'Date', href: '/products/date' }\n    ].filter(item => item.name.toLowerCase().includes(query)));\n  };\n\n  return (\n    <div class=\"search-container\">\n      <input\n        type=\"text\"\n        placeholder=\"Search fruits...\"\n        onInput={handleInput}\n        onKeyDown={onKeyDown}\n        aria-controls=\"search-results-list\"\n        aria-expanded={results().length > 0}\n        role=\"combobox\"\n      />\n      {results().length > 0 && (\n        <ul id=\"search-results-list\" role=\"listbox\">\n          <For each={results()}>\n            {(item) => (\n              <li\n                id={`item-${item.id}`}\n                role=\"option\"\n                aria-selected={active() === item.id}\n                tabIndex={active() === item.id ? 0 : -1}\n                onClick={() => setActive(item.id)}\n                onMouseEnter={() => setActive(item.id)} // for mouse navigation\n              >\n                <a href={item.href} tabIndex={-1}>{item.name}</a>\n              </li>\n            )}\n          </For>\n        </ul>\n      )}\n    </div>\n  );\n};","lang":"typescript","description":"This example demonstrates how to create a simple, keyboard-navigable search results list using `createList`. It includes basic reactivity for filtering results and correct event handling for keyboard and mouse interaction, along with ARIA attributes for accessibility."},"warnings":[{"fix":"Always explicitly specify `handleTab: true` or `handleTab: false` in the `createList` options to ensure consistent behavior.","message":"The documentation for the `handleTab` option in the README is contradictory. One line states `handleTab: false, // default = true`. It is safer to explicitly set `handleTab` to your desired behavior (`true` or `false`) rather than relying on an ambiguous default.","severity":"gotcha","affected_versions":">=0.3.0"},{"fix":"Consult the project's GitHub releases and changelog before upgrading minor versions to identify potential breaking changes and necessary migration steps.","message":"`solid-list` is currently in a pre-1.0 version (0.3.0). As such, breaking changes may be introduced in minor releases without strict adherence to semantic versioning until a stable 1.0 release. Always review changelogs when upgrading.","severity":"breaking","affected_versions":"<1.0.0"},{"fix":"Ensure the `items` option is a reactive accessor (a SolidJS signal or memo) that consistently returns an array of unique and stable IDs for your list elements.","message":"The `items` option for `createList` must be a function that returns an array of unique identifiers for your list items (e.g., `() => results().map(result => result.id)`). If the accessor is not reactive or does not return stable IDs, keyboard navigation or active state management might behave unpredictably.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure you are using ES module import syntax: `import { createList } from 'solid-list'` and that your build setup supports ESM.","cause":"Attempting to import `createList` using CommonJS `require()` syntax in an environment that expects ESM, or incorrect import path.","error":"TypeError: createList is not a function"},{"fix":"Attach `onKeyDown` to the parent container of your list or the input that triggers list interaction. Verify that the `items` option passed to `createList` is a reactive signal/memo returning unique IDs, and that your rendered list items have consistent `id` attributes that match these IDs.","cause":"The `onKeyDown` handler from `createList` is not attached to the correct element, or the `items` accessor is not reactive, or list items lack unique, stable `id`s.","error":"Keyboard navigation does not work or active item jumps erratically."}],"ecosystem":"npm"}