diff --git a/src/Connected.Components/Components/Dropdown.razor b/src/Connected.Components/Components/Dropdown.razor
new file mode 100644
index 0000000..99a445e
--- /dev/null
+++ b/src/Connected.Components/Components/Dropdown.razor
@@ -0,0 +1,48 @@
+@typeparam T;
+@inherits Connected.Models.InputBase;
+
+
\ No newline at end of file
diff --git a/src/Connected.Components/Components/Dropdown.razor.cs b/src/Connected.Components/Components/Dropdown.razor.cs
new file mode 100644
index 0000000..2c2780c
--- /dev/null
+++ b/src/Connected.Components/Components/Dropdown.razor.cs
@@ -0,0 +1,112 @@
+using Microsoft.AspNetCore.Components;
+using System.Collections.ObjectModel;
+using Connected.Models;
+using System.Collections.Immutable;
+
+namespace Connected.Components;
+
+public partial class Dropdown : InputBase, IAsyncDisposable
+{
+ [Parameter, EditorRequired]
+ public ObservableCollection Items { get; set; } = new ObservableCollection();
+
+ [Parameter]
+ public RenderFragment? OptionTemplate { get; set; }
+
+ [Parameter]
+ public RenderFragment? SelectedValueTemplate { get; set; }
+
+ [Parameter, EditorRequired]
+ public Func ItemToKey { get; set; } = default!;
+
+ [Parameter]
+ public ObservableCollection SelectedItems { get; set; } = new();
+
+ [Parameter]
+ public EventCallback> SelectedItemsChanged { get; set; }
+
+ [Parameter]
+ public bool AllowMultiple { get; set; }
+
+ private bool DropdownOpen { get; set; }
+
+ private bool IsItemSelected(T item)
+ {
+ return SelectedItems.Contains(item);
+ }
+
+ public void Open()
+ {
+ DropdownOpen = true;
+ }
+
+ public void Close()
+ {
+ DropdownOpen = false;
+ }
+
+ public void Toggle()
+ {
+ DropdownOpen = !DropdownOpen;
+ }
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ OptionTemplate ??= (T item) => (builder) =>
+ {
+ builder.OpenElement(0, "div");
+ builder.SetKey(item);
+ builder.AddContent(1, item?.ToString());
+ builder.CloseElement();
+ };
+
+ SelectedValueTemplate ??= (T item) => (builder) =>
+ {
+ builder.OpenElement(0, "span");
+ builder.SetKey(item);
+ builder.AddContent(1, item?.ToString());
+ builder.CloseElement();
+ };
+ }
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+
+ Items.CollectionChanged += CollectionChanged;
+ SelectedItems.CollectionChanged += CollectionChanged;
+ }
+
+ private void CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ if (sender == SelectedItems)
+ SelectedItemsChanged.InvokeAsync(SelectedItems);
+
+ StateHasChanged();
+ }
+
+ private async Task OptionSelected(T item)
+ {
+ if (AllowMultiple)
+ {
+ if (SelectedItems.Contains(item))
+ {
+ SelectedItems.Remove(item);
+ return;
+ }
+ }
+ else
+ {
+ SelectedItems.Clear();
+ }
+
+ SelectedItems.Add(item);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ Items.CollectionChanged -= CollectionChanged;
+ SelectedItems.CollectionChanged -= CollectionChanged;
+ }
+}
\ No newline at end of file