Basic example
The filter
option is required in order for component to work
properly. The option accepts a function that is expected to return an array
of results or a Promise
that resolves to an array of results.
<div class="relative" data-te-input-wrapper-init id="basic">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput1" />
<label
for="exampleFormControlInput1"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const basicAutocomplete = document.querySelector('#basic');
new Autocomplete(basicAutocomplete, {
filter: dataFilter,
});
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const basicAutocomplete = document.querySelector('#basic');
new te.Autocomplete(basicAutocomplete, {
filter: dataFilter,
});
Hey there 👋 we want to make TW elements a community-driven project. It's open source and free, and we would like it to stay that way. If you enjoy it, help the project grow by sharing it with your peers. Every share counts, thank you!
Display value
The displayValue
option allow to separate oryginal result value
from the value that will be displayed in the result list or input (after
selection). Its useful when the data returned by the
filter
function is an array of objects. You can specify which
parameter of the object should be displayed.
<div class="relative" data-te-input-wrapper-init id="displayValue">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput2" />
<label
for="exampleFormControlInput2"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const customData = [
{ title: 'One', subtitle: 'Secondary text' },
{ title: 'Two', subtitle: 'Secondary text' },
{ title: 'Three', subtitle: 'Secondary text' },
{ title: 'Four', subtitle: 'Secondary text' },
{ title: 'Five', subtitle: 'Secondary text' },
{ title: 'Six', subtitle: 'Secondary text' },
];
const customFilter = (value) => {
return customData.filter((item) => {
return item.title.toLowerCase().startsWith(value.toLowerCase());
});
};
const displayValueAutocomplete = document.querySelector('#displayValue');
new Autocomplete(displayValueAutocomplete, {
filter: customFilter,
displayValue: (value) => value.title,
});
const customData = [
{ title: 'One', subtitle: 'Secondary text' },
{ title: 'Two', subtitle: 'Secondary text' },
{ title: 'Three', subtitle: 'Secondary text' },
{ title: 'Four', subtitle: 'Secondary text' },
{ title: 'Five', subtitle: 'Secondary text' },
{ title: 'Six', subtitle: 'Secondary text' },
];
const customFilter = (value) => {
return customData.filter((item) => {
return item.title.toLowerCase().startsWith(value.toLowerCase());
});
};
const displayValueAutocomplete = document.querySelector('#displayValue');
new te.Autocomplete(displayValueAutocomplete, {
filter: customFilter,
displayValue: (value) => value.title,
});
Asynchronous search
The function passed to the filter
option can return a
Promise
that resolves to an array of results. By default the
component expects to receive data as an array of strings. If you want to
return an array of objects instead, you can use
displayValue
option to specify which parameter should be used
as a display value of the specific result.
<div class="relative" data-te-input-wrapper-init id="async">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput3" />
<label
for="exampleFormControlInput3"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const asyncAutocomplete = document.querySelector('#async');
const asyncFilter = async (query) => {
const url = `https://swapi.py4e.com/api/people/?search=${encodeURI(query)}`;
const response = await fetch(url);
const data = await response.json();
return data.results;
};
new Autocomplete(asyncAutocomplete, {
filter: asyncFilter,
displayValue: (value) => value.name
});
const asyncAutocomplete = document.querySelector('#async');
const asyncFilter = async (query) => {
const url = `https://swapi.py4e.com/api/people/?search=${encodeURI(query)}`;
const response = await fetch(url);
const data = await response.json();
return data.results;
};
new te.Autocomplete(asyncAutocomplete, {
filter: asyncFilter,
displayValue: (value) => value.name
});
Threshold
Use threshold
option to specify a minimum number of the
characters in the input field needed to perform a search operation.
<div class="relative" data-te-input-wrapper-init id="threshold">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput4"
placeholder="Type 2 characters to search" />
<label
for="exampleFormControlInput4"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const thresholdAutocomplete = document.querySelector('#threshold');
new Autocomplete(thresholdAutocomplete, {
filter: dataFilter,
threshold: 2
});
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const thresholdAutocomplete = document.querySelector('#threshold');
new te.Autocomplete(thresholdAutocomplete, {
filter: dataFilter,
threshold: 2
});
Custom item template
The itemContent
option allow to display custom HTML in the
result list. You can use the listHeight
option to modify the
result list height when you want to display more content in the component
dropdown.
<div
class="relative"
data-te-input-wrapper-init
id="customItem"
data-te-list-height="295">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput5" />
<label
for="exampleFormControlInput5"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const customData = [
{ title: 'One', subtitle: 'Secondary text' },
{ title: 'Two', subtitle: 'Secondary text' },
{ title: 'Three', subtitle: 'Secondary text' },
{ title: 'Four', subtitle: 'Secondary text' },
{ title: 'Five', subtitle: 'Secondary text' },
{ title: 'Six', subtitle: 'Secondary text' },
];
const customFilter = (value) => {
return customData.filter((item) => {
return item.title.toLowerCase().startsWith(value.toLowerCase());
});
};
const customItem = document.querySelector('#customItem');
new Autocomplete(customItem, {
filter: customFilter,
displayValue: (value) => value.title,
itemContent: (result) => {
return `
<div class="flex flex-col">
<div class="font-medium">${result.title}</div>
<div class="text-[0.9rem]">${result.subtitle}</div>
</div>
`;
},
});
const customData = [
{ title: 'One', subtitle: 'Secondary text' },
{ title: 'Two', subtitle: 'Secondary text' },
{ title: 'Three', subtitle: 'Secondary text' },
{ title: 'Four', subtitle: 'Secondary text' },
{ title: 'Five', subtitle: 'Secondary text' },
{ title: 'Six', subtitle: 'Secondary text' },
];
const customFilter = (value) => {
return customData.filter((item) => {
return item.title.toLowerCase().startsWith(value.toLowerCase());
});
};
const customItem = document.querySelector('#customItem');
new te.Autocomplete(customItem, {
filter: customFilter,
displayValue: (value) => value.title,
itemContent: (result) => {
return `
<div class="flex flex-col">
<div class="font-medium">${result.title}</div>
<div class="text-[0.9rem]">${result.subtitle}</div>
</div>
`;
},
});
Custom content
A custom content container with a
data-te-autocomplete-custom-content-ref
attribute will be
displayed at the end of the autocomplete dropdown. You can use it to display
a number of search results.
<div class="relative" data-te-input-wrapper-init id="customContent">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput6" />
<label
for="exampleFormControlInput6"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const customContent = document.querySelector('#customContent');
new Autocomplete(customContent, {
filter: dataFilter,
customContent: `
<div data-te-autocomplete-custom-content-ref class="px-4 py-1.5"></div>
`,
});
customContent.addEventListener("update.te.autocomplete", (event) => {
const resultsLength = event.results.length;
setTimeout(() => {
const customContentContainer = document.querySelector(
"[data-te-autocomplete-custom-content-ref]"
);
customContentContainer.innerHTML = `Search results: ${resultsLength}`;
}, 0);
});
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const customContent = document.querySelector('#customContent');
new te.Autocomplete(customContent, {
filter: dataFilter,
customContent: `
<div data-te-autocomplete-custom-content-ref class="px-4 py-1.5"></div>
`,
});
customContent.addEventListener("update.te.autocomplete", (event) => {
const resultsLength = event.results.length;
setTimeout(() => {
const customContentContainer = document.querySelector(
"[data-te-autocomplete-custom-content-ref]"
);
customContentContainer.innerHTML = `Search results: ${resultsLength}`;
}, 0);
});
Validation
You can use the validation component together with autocomplete.
<form
data-te-validation-init
data-te-active-validation="true"
autocomplete="off">
<div
class="relative"
id="validation"
data-te-input-wrapper-init
data-te-validate="input"
data-te-validation-ruleset="isRequired">
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput7" />
<label
for="exampleFormControlInput7"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
<button
type="button"
class="mt-5 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-[0_4px_9px_-4px_#3b71ca] transition duration-150 ease-in-out hover:bg-primary-600 hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:bg-primary-600 focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:outline-none focus:ring-0 active:bg-primary-700 active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] dark:shadow-[0_4px_9px_-4px_rgba(59,113,202,0.5)] dark:hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)]"
data-te-submit-btn-ref>
Submit
</button>
</form>
import { Autocomplete, Input, Validation, initTE } from "tw-elements";
initTE({ Input, Validation })
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const validationAutocomplete = document.querySelector('#validation');
new Autocomplete(validationAutocomplete, {
filter: dataFilter
});
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const validationAutocomplete = document.querySelector('#validation');
new te.Autocomplete(validationAutocomplete, {
filter: dataFilter
});
Auto select
Set autoSelect
option to true
to enable selecting
on Tab press.
<div class="relative" id="auto-select" data-te-input-wrapper-init>
<input
type="text"
class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear placeholder:opacity-0 focus:placeholder:opacity-100 peer-focus:text-primary motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary"
id="exampleFormControlInput8" />
<label
for="exampleFormControlInput8"
class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[80%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-focused]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-focused]:scale-[0.8] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>Example label
</label>
</div>
import { Autocomplete, Input, initTE } from "tw-elements";
initTE({ Input });
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const autoSelectAutocomplete = document.querySelector('#auto-select');
new Autocomplete(autoSelectAutocomplete, {
filter: dataFilter,
autoSelect: true
});
const data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
const autoSelectAutocomplete = document.querySelector('#auto-select');
new te.Autocomplete(autoSelectAutocomplete, {
filter: dataFilter,
autoSelect: true
});