diff --git a/library/src/main/java/dev/percula/ktx/event/Event.kt b/library/src/main/java/dev/percula/ktx/event/Event.kt
new file mode 100644
index 0000000..01ba0ff
--- /dev/null
+++ b/library/src/main/java/dev/percula/ktx/event/Event.kt
@@ -0,0 +1,27 @@
+package dev.percula.ktx.event
+
+/**
+ * Used as a wrapper for data that is exposed via a LiveData that represents an event (one-time use).
+ */
+open class Event<out T>(private val content: T) {
+
+    var hasBeenHandled = false
+        private set // Allow external read but not write
+
+    /**
+     * Returns the content and prevents its use again.
+     */
+    fun getContentIfNotHandled(): T? {
+        return if (hasBeenHandled) {
+            null
+        } else {
+            hasBeenHandled = true
+            content
+        }
+    }
+
+    /**
+     * Returns the content, even if it's already been handled.
+     */
+    fun peekContent(): T = content
+}
\ No newline at end of file
diff --git a/library/src/main/java/dev/percula/ktx/event/EventObserver.kt b/library/src/main/java/dev/percula/ktx/event/EventObserver.kt
new file mode 100644
index 0000000..21e6da9
--- /dev/null
+++ b/library/src/main/java/dev/percula/ktx/event/EventObserver.kt
@@ -0,0 +1,15 @@
+package dev.percula.ktx.event
+
+import androidx.lifecycle.Observer
+
+/**
+ * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
+ * already been handled.
+ *
+ * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
+ */
+class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
+    override fun onChanged(event: Event<T>?) {
+        event?.getContentIfNotHandled()?.let { onEventUnhandledContent }
+    }
+}
\ No newline at end of file