diff --git a/ignite-html.js b/ignite-html.js
index 2d9950c..5c8bf48 100644
--- a/ignite-html.js
+++ b/ignite-html.js
@@ -456,7 +456,12 @@ class IgniteObject {
  * @ignore
  */
 class IgniteCallback {
-    constructor(callback, detach) {
+    /**
+     * Creates a new IgniteCallback with a callback and optional detach callback.
+     * @param {Function(...)} callback The callback function to invoke when the callback is invoked.
+     * @param {Function(IgniteCallback)} detach The detach function that is called when the callback is disconnected.
+     */
+    constructor(callback, detach = null) {
         this.callback = callback;
         this.detach = detach;
     }
diff --git a/ignite-template.js b/ignite-template.js
index 1c6a427..2928ec1 100644
--- a/ignite-template.js
+++ b/ignite-template.js
@@ -2119,10 +2119,12 @@ class html extends IgniteTemplate {
 class list extends IgniteTemplate {
     /**
      * @param {Array|IgniteProperty} list The list of items to construct within this template.
-     * @param {Function(item, index, count)} forEach A function that constructs a template foreach item.
+     * @param {Function(item, index, count)} forEachCallback A function that constructs a template foreach item.
      * @param {Boolean} reflect If true any items removed from the DOM will be removed from the list if they exist. By default this is false.
+     * @param {Function} refreshCallback A function that is called when the user is requesting the list to refresh.
+     * @param {IgniteHtmlElement} refreshCallback A function that is called when the user is requesting the list to refresh.
      */
-    constructor(list, forEach, reflect = false) {
+    constructor(list, forEachCallback, reflect = false, refreshCallback = null, refreshPlaceholderElement = null) {
         super();
 
         if (list instanceof IgniteProperty) {
@@ -2140,7 +2142,13 @@ class list extends IgniteTemplate {
 
         this.reflecting = reflect;
         this.reflectCallbacks = [];
-        this.forEach = forEach;
+        this.forEachCallback = forEachCallback;
+        this.refreshCallback = refreshCallback;
+        this.refreshPlaceholderElement = refreshPlaceholderElement;
+        this.refreshTouchStartY = 0;
+        this.refreshTouchStartListener = (e) => this.onRefreshTouchStart(e);
+        this.refreshTouchMoveListener = (e) => this.onRefreshTouchMove(e);
+        this.refreshTouchEndListener = (e) => this.onRefreshTouchEnd(e);
         this.elements = [];
         this.tagName = "shadow list";
     }
@@ -2159,6 +2167,23 @@ class list extends IgniteTemplate {
             } else {
                 parent.appendChild(this.element);
             }
+
+            if (this.refreshCallback) {
+                if (!this.refreshPlaceholderElement) {
+                    this.refreshPlaceholderElement = document.createElement("div");
+                    this.refreshPlaceholderElement.classList.add("ignite-html");
+                    this.refreshPlaceholderElement.classList.add("refresh-placeholder");
+                    this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px");
+                    this.refreshPlaceholderElement.element.style.setProperty("height", "0px");
+                    parent.prepend(this.refreshPlaceholderElement);
+                } else {
+                    this.refreshPlaceholderElement.construct(parent, this.element);
+                    this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px");
+                    this.refreshPlaceholderElement.element.style.setProperty("height", "0px");
+                }
+
+                parent.addEventListener("touchstart", this.refreshTouchStartListener);
+            }
         } else {
             parent = this.element.parentElement;
         }
@@ -2192,7 +2217,7 @@ class list extends IgniteTemplate {
         //Construct all the items in our list and use the container
         if (this.list) {
             for (var i = 0; i < this.list.length; i++) {
-                var template = this.forEach(this.list[i], i, this.list.length);
+                var template = this.forEachCallback(this.list[i], i, this.list.length);
                 if (template) {
                     template.construct(parent, this.element);
 
@@ -2233,7 +2258,7 @@ class list extends IgniteTemplate {
 
         try {
             items.forEach(item => {
-                var template = this.forEach(item, this.children.length);
+                var template = this.forEachCallback(item, this.children.length);
 
                 if (this.elements.length > 0) {
                     template.construct(this.element.parentElement, this.elements[this.elements.length - 1].nextSibling);
@@ -2262,7 +2287,7 @@ class list extends IgniteTemplate {
         try {
             items.reverse();
             items.forEach(item => {
-                var template = this.forEach(item, 0);
+                var template = this.forEachCallback(item, 0);
 
                 if (this.elements.length > 0) {
                     template.construct(this.element.parentElement, this.elements[0]);
@@ -2339,7 +2364,7 @@ class list extends IgniteTemplate {
         //Append any new items if there are any.
         if (items) {
             items.forEach(item => {
-                var template = this.forEach(item, start);
+                var template = this.forEachCallback(item, start);
 
                 if (this.elements.length > 0) {
                     template.construct(this.element.parentElement, this.elements[start]);
@@ -2369,6 +2394,55 @@ class list extends IgniteTemplate {
         }
     }
 
+    onRefreshTouchStart(event) {
+        var touch = event.touches[0];
+
+        this.refreshTouchStartY = touch.clientY;
+
+        this.element.parentElement.addEventListener("touchmove", this.refreshTouchMoveListener);
+        this.element.parentElement.addEventListener("touchend", this.refreshTouchEndListener);
+    }
+
+    onRefreshTouchMove(event) {
+        var touch = event.touches[0];
+        var diff = Math.max(touch.clientY - this.refreshTouchStartY, 0);
+
+        if (diff < 100) {
+            if (this.refreshPlaceholderElement instanceof IgniteTemplate) {
+                this.refreshPlaceholderElement.element.style.setProperty("min-height", `${diff}px`);
+                this.refreshPlaceholderElement.element.style.setProperty("height", `${diff}px`);
+            } else {
+                this.refreshPlaceholderElement.style.setProperty("min-height", `${diff}px`);
+                this.refreshPlaceholderElement.style.setProperty("height", `${diff}px`);
+            }
+        } else {
+            if (this.refreshPlaceholderElement instanceof IgniteTemplate) {
+                this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px");
+                this.refreshPlaceholderElement.element.style.setProperty("height", "0px");
+            } else {
+                this.refreshPlaceholderElement.style.setProperty("min-height", "0px");
+                this.refreshPlaceholderElement.style.setProperty("height", "0px");
+            }
+
+            this.element.parentElement.removeEventListener("touchmove", this.refreshTouchMoveListener);
+
+            this.refreshCallback();
+        }
+    }
+
+    onRefreshTouchEnd(event) {
+        if (this.refreshPlaceholderElement instanceof IgniteTemplate) {
+            this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px");
+            this.refreshPlaceholderElement.element.style.setProperty("height", "0px");
+        } else {
+            this.refreshPlaceholderElement.style.setProperty("min-height", "0px");
+            this.refreshPlaceholderElement.style.setProperty("height", "0px");
+        }
+
+        this.element.parentElement.removeEventListener("touchmove", this.refreshTouchMoveListener);
+        this.element.parentElement.removeEventListener("touchend", this.refreshTouchEndListener);
+    }
+
     onStyleChanged(name, newValue) {
         this.elements.forEach((element) => {
             element.style.setProperty(name, newValue, this._styles[name].priority);