{"id":17717161,"url":"https://github.com/form-atoms/list-atom","last_synced_at":"2025-08-02T05:08:11.116Z","repository":{"id":225842911,"uuid":"767014000","full_name":"form-atoms/list-atom","owner":"form-atoms","description":"A listAtom extension for the Jotai form-atoms.","archived":false,"fork":false,"pushed_at":"2024-09-13T09:31:48.000Z","size":1749,"stargazers_count":4,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-12T13:05:50.333Z","etag":null,"topics":["fieldarray","form-atoms","jotai-form","react-form"],"latest_commit_sha":null,"homepage":"https://form-atoms.github.io/list-atom/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/form-atoms.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-04T14:51:50.000Z","updated_at":"2024-09-13T09:31:51.000Z","dependencies_parsed_at":"2024-09-13T21:15:31.386Z","dependency_job_id":"fbb36990-85ef-4e4b-af1b-5c57d1444e5e","html_url":"https://github.com/form-atoms/list-atom","commit_stats":null,"previous_names":["form-atoms/list-atom"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/form-atoms/list-atom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/form-atoms%2Flist-atom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/form-atoms%2Flist-atom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/form-atoms%2Flist-atom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/form-atoms%2Flist-atom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/form-atoms","download_url":"https://codeload.github.com/form-atoms/list-atom/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/form-atoms%2Flist-atom/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268338034,"owners_count":24234539,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fieldarray","form-atoms","jotai-form","react-form"],"created_at":"2024-10-25T14:12:19.105Z","updated_at":"2025-08-02T05:08:10.325Z","avatar_url":"https://github.com/form-atoms.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg height=\"300\" style=\"margin: 32px\" src=\"./public/form-atoms-banner-transparent.png#gh-dark-mode-only\"\u003e\n  \u003cimg width=\"180\" style=\"margin: 32px\" src=\"./public/form-atoms-field.svg#gh-light-mode-only\"\u003e\n  \u003ch1\u003elist-atom extension for the \u003ca href=\"https://github.com/form-atoms/form-atoms\" target=\"_blank\"\u003eform-atoms\u003c/a\u003e\u003c/h1\u003e\n\u003c/div\u003e\n\n```sh\nnpm install jotai-effect @form-atoms/list-atom\n```\n\n\u003ca aria-label=\"Minzipped size\" href=\"https://bundlephobia.com/result?p=%40form-atoms/list-atom\"\u003e\n  \u003cimg alt=\"Bundlephobia\" src=\"https://img.shields.io/bundlephobia/minzip/%40form-atoms/list-atom?style=for-the-badge\u0026labelColor=24292e\"\u003e\n\u003c/a\u003e\n\u003ca aria-label=\"NPM version\" href=\"https://www.npmjs.com/package/%40form-atoms/list-atom\"\u003e\n  \u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/v/%40form-atoms/list-atom?style=for-the-badge\u0026labelColor=24292e\"\u003e\n\u003c/a\u003e\n\u003ca aria-label=\"Code coverage report\" href=\"https://codecov.io/gh/form-atoms/list-atom\"\u003e\n  \u003cimg alt=\"Code coverage\" src=\"https://img.shields.io/codecov/c/gh/form-atoms/list-atom?style=for-the-badge\u0026labelColor=24292e\"\u003e\n\u003c/a\u003e\n\n### Quick start\n\n```tsx\nimport { fromAtom, useForm, fieldAtom, InputField } from \"form-atoms\";\nimport { listAtom, List } from \"@form-atoms/list-atom\";\n\nconst environmentVariables = listAtom({\n  name: \"environment\",\n  value: [{ key: \"GITHUB_SECRET\", value: \"\u003chash\u003e\" }],\n  fields: ({ key, value }) =\u003e ({\n    key: fieldAtom({ value: key }),\n    value: fieldAtom({ value }),\n  }),\n});\n\nconst form = formAtom({ environmentVariables });\n\nexport const Form = () =\u003e {\n  const { submit } = useForm(form);\n\n  return (\n    \u003cform onSubmit={submit(console.log)}\u003e\n      \u003cList atom={environmentVariables}\u003e\n        {({ fields }) =\u003e (\n          \u003c\u003e\n            \u003cInputField\n              atom={fields.key}\n              render={(props) =\u003e (\n                \u003c\u003e\n                  \u003clabel\u003eVariable Key\u003c/label\u003e\n                  \u003cinput {...props} /\u003e\n                \u003c/\u003e\n              )}\n            /\u003e\n            \u003cInputField\n              atom={fields.value}\n              render={(props) =\u003e (\n                \u003c\u003e\n                  \u003clabel\u003eVariable Value\u003c/label\u003e\n                  \u003cinput {...props} /\u003e\n                \u003c/\u003e\n              )}\n            /\u003e\n          \u003c/\u003e\n        )}\n      \u003c/List\u003e\n    \u003c/form\u003e\n  );\n};\n```\n\n## Table of contents\n\n| Atoms                     | Description                                                                                                                                                   |\n| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [`listAtom()`](#listatom) | An atom that represents a list of form fields in a form. It manages state for the list, including the name, value, errors, dirty, validation and empty state. |\n\n| Hooks                                 | Description                                                                                                    |\n| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |\n| [`useListActions()`](#uselistactions) | A hook that returns a `add`, `remove` \u0026 `move` actions, that can be used to interact with the list atom state. |\n| [`useList()`](#uselist)               | A hook that returns the list `items` ready to be rendred together with the list actions.                       |\n\n| Components        | Description                                                                                                                            |\n| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| [`\u003cList\u003e`](#list) | A component that render the individual items of listAtom with render props to render `AddButton`, `RemoveButton` and/or `Empty` slate. |\n\n## List atoms\n\n### listAtom()\n\nAn atom that represents a **list of form fields** in a form. It manages state for the list, including the name, value, errors, dirty, validation and empty state.\n\n#### Arguments\n\n| Name   | Type                                               | Required? | Description                                       |\n| ------ | -------------------------------------------------- | --------- | ------------------------------------------------- |\n| config | [`ListAtomConfig\u003cFields, Value\u003e`](#ListAtomConfig) | Yes       | The initial state and configuration of the field. |\n\n#### `ListAtomConfig`\n\n```ts\nexport type ListAtomConfig\u003cFields extends FormFields, Value\u003e = {\n  /**\n   * Optionally provide a name for the field that will be added\n   * prefixed to inner fields\n   * E.g. list name \"contacts\" and field name \"email\"\n   * will have scoped name for the 4th item \"contacts[3].email\"\n   */\n  name?: string;\n  /**\n   * The initial array of values of the list\n   */\n  value: Value[];\n  /**\n   * A function to initialize the fields for each of the initial values.\n   */\n  fields: (value: Value) =\u003e Fields;\n  /**\n   * Error message the listAtom will have, when its items have nested errors.\n   * It will be one of errors returned by the `useFieldErrors()` hook.\n   */\n  invalidItemError?: string;\n  /**\n   * A function that validates the value of the field any time\n   * one of its atoms changes. It must either return an array of\n   * string error messages or undefined. If it returns undefined,\n   * the validation is \"skipped\" and the current errors in state\n   * are retained.\n   */\n  validate?: (state: {\n    /**\n     * A Jotai getter that can read other atoms\n     */\n    get: Getter;\n    /**\n     * The current value of the field\n     */\n    value: Value;\n    /**\n     * The dirty state of the field\n     */\n    dirty: boolean;\n    /**\n     * The touched state of the field\n     */\n    touched: boolean;\n    /**\n     * The event that caused the validation. Either:\n     *\n     * - `\"change\"` - The value of the field has changed\n     * - `\"touch\"` - The field has been touched\n     * - `\"blur\"` - The field has been blurred\n     * - `\"submit\"` - The form has been submitted\n     * - `\"user\"` - A user/developer has triggered the validation\n     */\n    event: ValidateOn;\n  }) =\u003e void | string[] | Promise\u003cvoid | string[]\u003e;\n};\n```\n\n#### Returns\n\nAn extended `FieldAtom`:\n\n```ts\nexport type ListAtom\u003cFields extends FormFields, Value\u003e = ExtendFieldAtom\u003c\n  Value[],\n  {\n    /**\n     * An atom indicating whether the list is empty.\n     */\n    empty: Atom\u003cboolean\u003e;\n    /**\n     * A splitAtom() instance from jotai/utils.\n     * It handles adding, removing and moving of items in the list.\n     * @internal\n     */\n    _splitList: WritableAtom\u003c\n      PrimitiveAtom\u003cListItemForm\u003cFields\u003e\u003e[],\n      [SplitAtomAction\u003cListItemForm\u003cFields\u003e\u003e],\n      void\n    \u003e;\n    /**\n     * An atom holding the list of forms of each item.\n     * @internal\n     */\n    _formList: WritableAtom\u003c\n      ListItemForm\u003cFields\u003e[],\n      [typeof RESET | SetStateAction\u003cListItemForm\u003cFields\u003e[]\u003e],\n      void\n    \u003e;\n    /**\n     * An atom holding the fields of the internal formAtom of each item.\n     * @internal\n     */\n    _formFields: Atom\u003cFields[]\u003e;\n    buildItem(): ListItemForm\u003cFields\u003e;\n  }\n\u003e;\n```\n\n#### [⇗ Back to top](#table-of-contents)\n\n## Hooks\n\n### useListActions()\n\nA hook that returns a `add`, `remove` \u0026 `move` actions, that can be used to interact with the list atom state.\n\n#### Arguments\n\n| Name     | Type                                         | Required? | Description                                                                         |\n| -------- | -------------------------------------------- | --------- | ----------------------------------------------------------------------------------- |\n| listAtom | `ListAtom\u003cFields extends FormFields, Value\u003e` | Yes       | The atom that stores the list's state                                               |\n| options  | `UseAtomOptions`                             | No        | Options that are forwarded to the `useAtom`, `useAtomValue`, and `useSetAtom` hooks |\n\n#### Returns\n\n```ts\nexport type UseListActions\u003cFields extends FormFields\u003e = {\n  /**\n   * Removes the item from the list.\n   *\n   * @param item - An item from the listAtom's splitList array.\n   */\n  remove: (item: ListItem\u003cFields\u003e) =\u003e void;\n  /**\n   * Appends a new item to the list by default, when no 'before' position is used.\n   * Optionally the item can be initialized, with the 'fields' argument.\n   *\n   * @param before - An item from the listAtom's splitList array.\n   * @param fields - A custom initialized fieldAtoms matching the Fields shape of the list.\n   */\n  add: (\n    before?: ListItem\u003cFields\u003e | undefined,\n    fields?: Fields | undefined,\n  ) =\u003e void;\n  /**\n   * Moves the item to the end of the list, or where specified when the 'before' is defined.\n   *\n   * @param item - A splitList item to be moved.\n   * @param before - A splitList item before which to place the moved item.\n   */\n  move: (item: ListItem\u003cFields\u003e, before?: ListItem\u003cFields\u003e | undefined) =\u003e void;\n};\n```\n\n#### [⇗ Back to top](#table-of-contents)\n\n### useList()\n\nA hook that returns the list `items` ready to be rendred together with the list actions.\n\n#### Arguments\n\n| Name     | Type                                         | Required? | Description                                                                         |\n| -------- | -------------------------------------------- | --------- | ----------------------------------------------------------------------------------- |\n| listAtom | `ListAtom\u003cFields extends FormFields, Value\u003e` | Yes       | The atom that stores the list's state                                               |\n| options  | `UseAtomOptions`                             | No        | Options that are forwarded to the `useAtom`, `useAtomValue`, and `useSetAtom` hooks |\n\n#### Returns\n\n```ts\nexport type UseList\u003cFields extends FormFields\u003e = UseListActions\u003cFields\u003e \u0026 {\n  /**\n   * Resolved value from the list.empty atom.\n   */\n  isEmpty: boolean;\n  items: {\n    /**\n     * The item from the internal splitList.\n     */\n    item: ListItem\u003cFields\u003e;\n    /**\n     * Stable React key prop derived from atom id.\n     */\n    key: string;\n    /**\n     * The form fields of the current item.\n     */\n    fields: Fields;\n    /**\n     * A function to remove the current item from the list.\n     */\n    remove: () =\u003e void;\n    /**\n     * A helper function to move the item to the previous index in the list.\n     */\n    moveUp: () =\u003e void;\n    /**\n     * A helper function to move the item to the next index in the list.\n     */\n    moveDown: () =\u003e void;\n  };\n};\n```\n\n#### [⇗ Back to top](#table-of-contents)\n\n## Components\n\n### \u0026lt;List\u0026gt;\n\n#### Props\n\n| Name         | Type                                                  | Required? | Description                                                               |\n| ------------ | ----------------------------------------------------- | --------- | ------------------------------------------------------------------------- |\n| atom         | `ListAtom\u003cFormFields, Value\u003e`                         | Yes       | A list atom                                                               |\n| children     | `(props: ListItemProps) =\u003e JSX.Element`               | Yes       | A render prop                                                             |\n| initialValue | `Value[]`                                             | No        | A value to (re)initialize the listAtom                                    |\n| RemoveButton | `FunctionComponent\u003c{remove: () =\u003e void}\u003e`             | no        | A render prop receiving `remove` function for each individual list item   |\n| AddButton    | `FunctionComponent\u003c{add: (fields?: Fields) =\u003e void}\u003e` | No        | A render prop to render a button adding new items to the end of the list  |\n| Empty        | `FunctionComponent`                                   | No        | A render prop to render a blank slate when there are no items in the list |\n| store        | `AtomStore`                                           | No        | [A Jotai store](https://jotai.org/docs/api/core#createstore)              |\n\n#### Render Props\n\nYour `children` render prop will receive the following props:\n\n```ts\ntype ListItemProps\u003cFields extends FormFields\u003e = {\n  /**\n   * The fields of current item, as returned from the builder function.\n   */\n  fields: Fields;\n  /**\n   * The item from the internal splitList.\n   */\n  item: ListItem\u003cFields\u003e;\n  /**\n   * The index of the current item.\n   */\n  index: number;\n  /**\n   * Total count of items in the list.\n   */\n  count: number;\n  /**\n   * Append a new item to the end of the list.\n   * When called with current item, it will be prepend with a new item.\n   */\n  add: (before?: ListItem\u003cFields\u003e) =\u003e void;\n  /**\n   * Removes the current item.\n   */\n  remove: () =\u003e void;\n  /**\n   * Moves the current item one slot up in the list.\n   * When called for the first item, the action is no-op.\n   */\n  moveUp: () =\u003e void;\n  /**\n   * Moves the current item one slot down in the list.\n   * When called for the last item, the item moves to the start of the list.\n   */\n  moveDown: () =\u003e void;\n  /**\n   * A component with an onClick handler bound to remove the current item from the list.\n   */\n  RemoveButton: FunctionComponent;\n};\n```\n\n#### [⇗ Back to top](#table-of-contents)\n\n## Let's see what's in this listAtom\n\n![atom in atoms](./public//atom-in-atom.png)\n\n#### [⇗ Back to top](#table-of-contents)\n\n## LICENSE\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fform-atoms%2Flist-atom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fform-atoms%2Flist-atom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fform-atoms%2Flist-atom/lists"}