{"id":17488486,"url":"https://github.com/smastrom/react-email-autocomplete","last_synced_at":"2025-04-22T15:41:17.467Z","repository":{"id":64387238,"uuid":"537761836","full_name":"smastrom/react-email-autocomplete","owner":"smastrom","description":"💌 Headless email input field with custom suggestions. Inspired by european flight booking websites.","archived":false,"fork":false,"pushed_at":"2023-12-10T21:39:53.000Z","size":422,"stargazers_count":15,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T08:15:02.723Z","etag":null,"topics":["react","react-autocomplete","react-autosuggest","react-email","react-email-component"],"latest_commit_sha":null,"homepage":"https://react-email-autocomplete.netlify.app","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/smastrom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2022-09-17T09:38:41.000Z","updated_at":"2025-01-12T18:22:02.000Z","dependencies_parsed_at":"2023-12-10T01:42:55.364Z","dependency_job_id":"7a8ec0a0-6b6c-48e6-af3f-4a44a3498800","html_url":"https://github.com/smastrom/react-email-autocomplete","commit_stats":{"total_commits":83,"total_committers":1,"mean_commits":83.0,"dds":0.0,"last_synced_commit":"bcb439365c0b79f85bf277157c4a344bd5d9d04c"},"previous_names":["smastrom/react-email-autocomplete"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-email-autocomplete","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-email-autocomplete/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-email-autocomplete/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smastrom%2Freact-email-autocomplete/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smastrom","download_url":"https://codeload.github.com/smastrom/react-email-autocomplete/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248625499,"owners_count":21135513,"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","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":["react","react-autocomplete","react-autosuggest","react-email","react-email-component"],"created_at":"2024-10-19T04:08:01.642Z","updated_at":"2025-04-16T20:32:14.970Z","avatar_url":"https://github.com/smastrom.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Email Autocomplete\n\n![npm](https://img.shields.io/npm/v/@smastrom/react-email-autocomplete?color=46c119) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/smastrom/react-email-autocomplete/tests.yml?branch=main\u0026label=tests)\n![dependency-count](https://img.shields.io/badge/dependency%20count-0-success)\n\n| Before typing `@`                                                                                       | After typing `@` (optional)                                                                             |\n| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| ![@smastrom/react-email-autocomplete](https://i.ibb.co/SNTkHJQ/Screenshot-2022-12-07-alle-13-50-59.png) | ![@smastrom/react-email-autocomplete](https://i.ibb.co/DWQBQw7/Screenshot-2022-12-07-alle-13-54-23.png) |\n\n\u003cbr /\u003e\n\n**React Email Autocomplete** is an unstyled, zero-dependency component inspired by some european flight booking websites. As soon as users start typing their email address, it will suggest the most common email providers.\n\n- Completely unstyled and white labeled (ships with zero CSS)\n- Fully accessible with superlative keyboard controls\n- Forward any event and attribute to the `\u003cinput /\u003e` element or control it with React Hook Form\n\n[Demo and examples](https://@smastrom/react-email-autocomplete.netlify.app) — [Stackblitz](https://stackblitz.com/edit/react-4kufqv?file=src/App.js) — [NextJS](https://stackblitz.com/edit/stackblitz-starters-f36nmm?file=app%2Fpage.tsx)\n\n\u003cbr /\u003e\n\n## :floppy_disk: Installation\n\n```bash\npnpm add @smastrom/react-email-autocomplete\n# npm i @smastrom/react-email-autocomplete\n# yarn add @smastrom/react-email-autocomplete\n```\n\n\u003cbr /\u003e\n\n## :art: Usage / Styling\n\nThe component renders a single `div` with a very simple structure:\n\n```js\nWrapper — div\n├── Email Input Field — input\n└── Dropdown — ul\n    └── Suggestions - li[]\n        └──[username - span:first-of-type] [@domain.com - span:last-of-type]\n```\n\nSpecify `classNames` for each element you'd like to style:\n\n```jsx\nimport { Email } from '@smastrom/react-email-autocomplete'\n\nconst classNames = {\n  wrapper: 'my-wrapper',\n  input: 'my-input',\n  dropdown: 'my-dropdown',\n  suggestion: 'my-suggestion',\n  username: 'my-username',\n  domain: 'my-domain',\n}\n\nconst baseList = ['gmail.com', 'yahoo.com', 'hotmail.com', 'aol.com', 'msn.com']\n\nfunction App() {\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmail\n      classNames={classNames}\n      baseList={baseList}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      value={email}\n    /\u003e\n  )\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eNextJS App Router\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n`components/Email.tsx`\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\nimport { Email as EmailAutocomplete } from '@smastrom/react-email-autocomplete'\n\nconst classNames = {\n  wrapper: 'my-wrapper',\n  input: 'my-input',\n  dropdown: 'my-dropdown',\n  suggestion: 'my-suggestion',\n  username: 'my-username',\n  domain: 'my-domain',\n}\n\nconst baseList = ['gmail.com', 'yahoo.com', 'hotmail.com', 'aol.com', 'msn.com']\n\nexport function Email() {\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmailAutocomplete\n      classNames={classNames}\n      baseList={baseList}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      value={email}\n    /\u003e\n  )\n}\n```\n\n`app/page.tsx`\n\n```tsx\nimport { Email } from '@/components/Email'\n\nexport default function Home() {\n  return (\n    \u003cmain\u003e\n      {/* ... */}\n      \u003cEmail /\u003e\n      {/* ... */}\n    \u003c/main\u003e\n  )\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eTypeScript\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```ts\nimport type { ClassNames } from '@smastrom/react-email-autocomplete'\n\nconst myClassNames: ClassNames = {\n  wrapper: 'my-wrapper',\n  input: 'my-input',\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eTailwind Intellisense\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\nYou can add a this property in VSCode's `settings.json` in order to enable autcomplete for any object property or variable ending with `ClassNames`.\n\n```json\n  \"tailwindCSS.experimental.classRegex\": [\n    [\"ClassNames \\\\=([^;]*);\", \"'([^']*)'\"],\n    [\"ClassNames \\\\=([^;]*);\", \"\\\"([^\\\"]*)\\\"\"],\n    [\"ClassNames \\\\=([^;]*);\", \"\\\\`([^\\\\`]*)\\\\`\"]\n  ],\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eBasic styles\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cbr /\u003e\n\nThis package ships with **zero css**. Initial styles enough to see the component in action may match the following properties:\n\n```css\n.my-wrapper,\n.my-input {\n  position: relative;\n}\n\n.my-input,\n.my-dropdown,\n.my-suggestion {\n  font-size: inherit;\n  box-sizing: border-box;\n  width: 100%;\n}\n\n.my-dropdown {\n  position: absolute;\n  margin: 0.45rem 0 0 0;\n  padding: 0;\n  list-style-type: none;\n  z-index: 999;\n}\n\n.my-suggestion {\n  cursor: pointer;\n  user-select: none;\n  overflow: hidden;\n}\n```\n\n\u003c/details\u003e\n\n### Focus/Hover styles\n\nAlthough you can target the pseudo classes `:hover` and `:focus`, it is recommended instead to target the attribute `data-active-email` in order to avoid `:hover` styles to be applied to a suggestion as soon as the dropdown is opened (in case the cursor is hovering it).\n\n```css\n.my-suggestion[data-active-email='true'] {\n  background-color: aliceblue;\n}\n\n.my-suggestion:hover,\n.my-suggestion:focus,\n.my-suggestion:focus-visible {\n  outline: none;\n}\n```\n\nThe attribute name can also be customized via `activeDataAttr` prop:\n\n```jsx\n\u003cEmail\n  activeDataAttr=\"data-custom-attr\"\n  classNames={{\n    ...classNames,\n    suggestion: 'my-suggestion',\n  }}\n  baseList={baseList}\n  value={email}\n/\u003e\n```\n\n```css\n.my-suggestion[data-custom-attr='true'] {\n  background-color: aliceblue;\n}\n```\n\n## :dna: Modes\n\n### 1. Basic Mode\n\nOnce users start typing, it displays a list of _base_ suggestions and hides it once they type `@` . It already gives a nice UX and should be enough for the vast majority of websites:\n\n| Before typing `@`                                                                                       | After typing `@`                                                                                        |\n| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| ![@smastrom/react-email-autocomplete](https://i.ibb.co/SNTkHJQ/Screenshot-2022-12-07-alle-13-50-59.png) | ![@smastrom/react-email-autocomplete](https://i.ibb.co/ZgWCPkg/Screenshot-2022-12-07-alle-13-52-46.png) |\n\n```jsx\nimport { Email } from '@smastrom/react-email-autocomplete'\n\nconst baseList = [\n  'gmail.com',\n  'yahoo.com',\n  'hotmail.com',\n  'aol.com',\n  'msn.com',\n  'proton.me',\n]\n\nfunction App() {\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmail\n      baseList={baseList}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      value={email}\n    /\u003e\n  )\n}\n```\n\n### 2. Refine Mode (optional)\n\nActs like **Basic Mode** until users type `@` . Then as they start typing the domain, it starts refining suggestions according to an extended list of domains.\n\n| Before typing `@`                                                                                       | After typing `@`                                                                                        |\n| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| ![@smastrom/react-email-autocomplete](https://i.ibb.co/SNTkHJQ/Screenshot-2022-12-07-alle-13-50-59.png) | ![@smastrom/react-email-autocomplete](https://i.ibb.co/DWQBQw7/Screenshot-2022-12-07-alle-13-54-23.png) |\n\nAll you have to do is to provide a second array of domains to `refineList` prop. This package ships with a [curated list](https://github.com/smastrom/@smastrom/react-email-autocomplete/blob/main/src/domains.json) of the ~160 most popular world domains that you can directly import and use (thanks to **@mailcheck**):\n\n```jsx\nimport { Email, domains } from '@smastrom/react-email-autocomplete'\n\nconst baseList = [\n  'gmail.com',\n  'yahoo.com',\n  'hotmail.com',\n  'aol.com',\n  'msn.com',\n  'proton.me',\n]\n\nfunction App() {\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmail\n      baseList={baseList}\n      refineList={domains}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      value={email}\n    /\u003e\n  )\n}\n```\n\nAlternatively, you can use your own array of domains or [search]() for the one that best suits your audience.\n\n\u003cbr /\u003e\n\n## :globe_with_meridians: Localization\n\nThis package ships with an optional hook that simplifies managing different lists of domains according to the [browser's locale](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language).\n\n**1 - Create an object and define lists for each browser locale:**\n\n```js\nexport const emailProviders = {\n  default: [\n    'gmail.com',\n    'yahoo.com',\n    'hotmail.com',\n    'aol.com',\n    // ...\n  ],\n  it: [\n    'gmail.com',\n    'yahoo.com',\n    'yahoo.it',\n    'tiscali.it',\n    // ...\n  ],\n  'it-CH': [\n    'gmail.com',\n    'outlook.com',\n    'bluewin.ch',\n    'gmx.de',\n    // ...\n  ],\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eTypeScript\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```ts\nimport type { LocalizedList } from '@smastrom/react-email-autocomplete'\n\nexport const emailProviders: LocalizedList = {\n  default: [\n    'gmail.com',\n    'yahoo.com',\n    'hotmail.com',\n    'aol.com',\n    // ...\n  ],\n  // ...\n}\n```\n\n\u003c/details\u003e\n\nYou may define [lang codes](https://www.localeplanet.com/icu/iso639.html) with or without country codes.\n\nFor languages without country code (such as `it`), by default it will match all browser locales beginning with it such as `it`, `it-CH`, `it-IT` and so on.\n\nFor languages with country code (`it-CH`) it will match `it-CH` but not `it` or `it-IT`.\n\nIf you define both `it-CH` and `it`, `it-CH` will match only `it-CH` and `it` will match `it`, `it-IT` and so on.\n\n**2 - Use the hook:**\n\n```jsx\nimport { Email, useLocalizedList } from '@smastrom/react-email-autocomplete'\nimport { emailProviders } from '@/src/static/locales'\n\nfunction App() {\n  const baseList = useLocalizedList(emailProviders)\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmail\n      baseList={baseList}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      value={email}\n    /\u003e\n  )\n}\n```\n\n### Usage with internationalization frameworks or SSR\n\nTo manually set the locale, pass its code as second argument:\n\n```jsx\nimport { useRouter } from 'next/router'\nimport { emailProviders } from '@/src/static/locales'\nimport { Email, useLocalizedList } from '@smastrom/react-email-autocomplete'\n\nfunction App() {\n  const { locale } = useRouter()\n  const baseList = useLocalizedList(emailProviders, locale)\n\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmail\n      baseList={baseList}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      value={email}\n    /\u003e\n  )\n}\n```\n\nOr with NextJS App router:\n\n`components/Email.tsx`\n\n```tsx\n'use client'\n\nimport {\n  Email as EmailAutocomplete,\n  useLocalizedList,\n} from '@smastrom/react-email-autocomplete'\nimport { emailProviders } from '@/static/locales'\n\nexport function Email({ lang }: { lang: string }) {\n  const baseList = useLocalizedList(emailProviders, lang)\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmailAutocomplete\n      classNames={classNames}\n      baseList={baseList}\n      onChange={setEmail}\n      value={email}\n    /\u003e\n  )\n}\n```\n\n`app/page.tsx`\n\n```tsx\nimport { Email } from '@/components/Email'\nimport { headers } from 'next/headers'\n\nexport default function Home() {\n  const headersList = headers()\n  const lang = headersList.get('accept-language')?.split(',')[0]\n\n  return (\n    \u003cmain\u003e\n      \u003cEmail lang={lang} /\u003e\n    \u003c/main\u003e\n  )\n}\n```\n\n\u003cbr /\u003e\n\n## :8ball: onSelect callback\n\nTo invoke a callback everytime a suggestion is selected (either with mouse or keyboard), pass a callback to `onSelect` prop:\n\n```jsx\nimport { Email } from '@smastrom/react-email-autocomplete'\n\nfunction handleSelect(data) {\n  console.log(data) // { value: 'johndoe@gmail.com', keyboard: true, position: 0 }\n}\n\nfunction App() {\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003cEmail\n      baseList={baseList}\n      onChange={setEmail} // or (newValue) =\u003e customSetter(newValue)\n      onSelect={handleSelect}\n      value={email}\n    /\u003e\n  )\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eType Definition\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr/\u003e\n\n```ts\ntype OnSelectData = {\n  value: string\n  keyboard: boolean\n  position: number\n}\n\ntype OnSelect = (object: OnSelectData) =\u003e void | Promise\u003cvoid\u003e\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n## :cyclone: Props\n\n| Prop                | Description                                           | Type                                   | Default             | Required           |\n| ------------------- | ----------------------------------------------------- | -------------------------------------- | ------------------- | ------------------ |\n| `value`             | State or portion of state that holds the email value  | _string_                               | undefined           | :white_check_mark: |\n| `onChange`          | State setter or custom dispatcher to update the email | _OnChange_                             | undefined           | :white_check_mark: |\n| `baseList`          | Domains to suggest while typing the username          | _string[]_                             | undefined           | :white_check_mark: |\n| `refineList`        | Domains to refine suggestions after typing `@`        | _string[]_                             | []                  | :x:                |\n| `onSelect`          | Custom callback on suggestion select                  | _OnSelect_                             | () =\u003e {}            | :x:                |\n| `minChars`          | Minimum chars required to display suggestions         | _1 \\| 2 \\| 3 \\| 4 \\| 5 \\| 6 \\| 7 \\| 8_ | 2                   | :x:                |\n| `maxResults`        | Maximum number of suggestions to display              | _2 \\| 3 \\| 4 \\| 5 \\| 6 \\| 7 \\| 8_      | 6                   | :x:                |\n| `classNames`        | Class names for each element                          | _ClassNames_                           | undefined           | :x:                |\n| `className`         | Class name of the root element                        | _string_                               | undefined           | :x:                |\n| `activeDataAttr`    | Attribute name to set on focused/hovered suggestion   | _string_                               | `data-active-email` | :x:                |\n| `dropdownAriaLabel` | Aria label for the dropdown list                      | _string_                               | `Suggestions`       | :x:                |\n\n:bulb: React's `ref` and any other `HTMLInputElement` attribute can be passed as prop to the component and it will be forwarded to the input element.\n\n\u003cbr /\u003e\n\n## :keyboard: Keyboard controls\n\n- **↑ ↓** - Navigate through suggestions / input\n- **← →** - Move cursor and focus the input field while keeping list open\n- **Backspace / Alphanumeric keys** - Edit the input value and keep refining suggestions\n- **Enter / Space** - Confirm the suggestion\n- **Escape** - Close the list and focus the input field\n- **Tab / Shift + Tab** - Close the list and go to next/prev focusable input\n\n\u003cbr /\u003e\n\n## React Hook Form\n\nNo special configuration needed, it just works. Just follow the official React Hook Form's [Controller documentation](https://react-hook-form.com/api/usecontroller/controller).\n\n\u003cbr /\u003e\n\n## :dvd: License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmastrom%2Freact-email-autocomplete","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmastrom%2Freact-email-autocomplete","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmastrom%2Freact-email-autocomplete/lists"}