single, static options — typing filters client-side with match highlight; ↑/↓ + Enter commit, Esc/blur revert free text; the selected option shows a check
value: —
filled variant, leading icon, supporting text, disabled option
multiple — chips + checkbox rows, menu stays open on
toggle, Backspace with empty input removes the last chip; posts one
form entry per value
values: ["bug"]
remote src — the server filters (?q=…,
debounced 250ms, stale requests aborted); JSON is
[{value,label}], {results:[…]} or
select2-style {id,text}. This demo mocks
/demo-api/countries with 400ms latency.
remote + multiple + min-chars="2" —
prompts until 2 characters are typed; selections made from earlier
result pages keep their labels via the value→label cache
form integration — form-associated (Django
request.POST.getlist('countries')), required validation,
reset restores initial selection
posted: —
RTL — label, chips, chevron and listbox mirror with the reading direction
error + disabled states
Sample usage (Django)
<!-- static: options in the template, filtered client-side -->
<material-autocomplete name="vendor" label="Vendor" required>
{% for v in vendors %}
<material-option value="{{ v.pk }}" {% if v == form.vendor.value %}selected{% endif %}>
{{ v.name }}
</material-option>
{% endfor %}
</material-autocomplete>
<!-- remote: a JSON view filters; render the current selection so the
label is known before the first fetch -->
<material-autocomplete name="assignee" label="Assignee"
src="{% url 'user-autocomplete' %}" min-chars="2">
{% if form.instance.assignee %}
<material-option value="{{ form.instance.assignee_id }}" selected>
{{ form.instance.assignee }}
</material-option>
{% endif %}
</material-autocomplete>
# the view — any of these shapes works, including select2's:
def user_autocomplete(request):
q = request.GET.get("q", "")
users = User.objects.filter(username__icontains=q)[:20]
return JsonResponse([{"value": u.pk, "label": u.username} for u in users], safe=False)
<script>
el.addEventListener('valueChange', (e) => e.detail); // {value, values}
el.addEventListener('materialSearch', (e) => e.detail); // {query} — event-driven mode
</script>