diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java
new file mode 100644
index 0000000..869721c
--- /dev/null
+++ b/src/main/java/envoy/client/ui/list/ComponentList.java
@@ -0,0 +1,32 @@
+package envoy.client.ui.list;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+/**
+ * Project: envoy-client
+ * File: ComponentList.java
+ * Created: 25.01.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy v0.3-alpha
+ */
+public class ComponentList extends JPanel {
+
+ private ComponentListCellRenderer renderer;
+
+ private static final long serialVersionUID = 1759644503942876737L;
+
+ public ComponentList(ComponentListModel model, ComponentListCellRenderer renderer) {
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ model.setComponentList(this);
+ this.renderer = renderer;
+
+ for (E elem : model)
+ add(renderer.getListCellComponent(this, elem, false));
+ }
+
+ void add(E elem) {
+ add(renderer.getListCellComponent(this, elem, false));
+ }
+}
diff --git a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java
new file mode 100644
index 0000000..6a3b383
--- /dev/null
+++ b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java
@@ -0,0 +1,16 @@
+package envoy.client.ui.list;
+
+import java.awt.Component;
+
+/**
+ * Project: envoy-client
+ * File: ComponentListCellRenderer.java
+ * Created: 25.01.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy v0.3-alpha
+ */
+public interface ComponentListCellRenderer {
+
+ Component getListCellComponent(ComponentList list, E value, boolean isSelected);
+}
diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/ComponentListModel.java
new file mode 100644
index 0000000..e5ea6fe
--- /dev/null
+++ b/src/main/java/envoy/client/ui/list/ComponentListModel.java
@@ -0,0 +1,83 @@
+package envoy.client.ui.list;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Project: envoy-client
+ * File: ComponentListModel.java
+ * Created: 25.01.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy v0.3-alpha
+ */
+public final class ComponentListModel implements Iterable {
+
+ private List elements = new ArrayList<>();
+ private ComponentList componentList;
+
+ /**
+ * Adds an element to this model and notifies the associated
+ * {@link ComponentList} to add the corresponding component.
+ *
+ * @param e the element to add
+ * @return {@code true}
+ * @see java.util.List#add(java.lang.Object)
+ */
+ public boolean add(E e) {
+ componentList.add(e);
+ return elements.add(e);
+ }
+
+ /**
+ * Removes all elements from this model and clears the associated
+ * {@link ComponentList}.
+ *
+ * @see java.util.List#clear()
+ * @since Envoy v0.3-alpha
+ */
+ public void clear() {
+ elements.clear();
+ componentList.removeAll();
+ }
+
+ /**
+ * @param index
+ * @return
+ * @see java.util.List#get(int)
+ */
+ public E get(int index) { return elements.get(index); }
+
+ /**
+ * Removes the element at a specific index from this model and the corresponding
+ * component from the {@link ComponentList}.
+ *
+ * @param index
+ * @return the removed element
+ * @see java.util.List#remove(int)
+ */
+ public E remove(int index) {
+ componentList.remove(index);
+ return elements.remove(index);
+ }
+
+ /**
+ * @return
+ * @see java.util.List#iterator()
+ */
+ public Iterator iterator() {
+ return new Iterator() {
+
+ Iterator iter = elements.iterator();
+
+ @Override
+ public boolean hasNext() { return iter.hasNext(); }
+
+ @Override
+ public E next() { return iter.next(); }
+ };
+ }
+
+ void setComponentList(ComponentList componentList) { this.componentList = componentList; }
+}