diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt
index bb383d54da00f5c1d434669061cf84c0d300202d..8d18fac04a80a7e261bb6e989b8f9255e1d24453 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt
@@ -77,6 +77,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   @BindView(R.id.autocomplete_list2)
   lateinit var autocompleteList2: RecyclerView
 
+  @BindView(R.id.msg_history)
+  lateinit var msgHistory: RecyclerView
+
   private lateinit var drawerToggle: ActionBarDrawerToggle
 
   private val handler = AndroidHandlerThread("Chat")
@@ -179,6 +182,20 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       }
     }
 
+    msgHistory.itemAnimator = DefaultItemAnimator()
+    msgHistory.layoutManager = LinearLayoutManager(this)
+    msgHistory.adapter = MessageHistoryAdapter(
+      this,
+      viewModel.recentlySentMessages,
+      handler::post,
+      ::runOnUiThread,
+      { text ->
+        chatline.setText(text)
+        chatline.setSelection(chatline.length())
+        historyPanel.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
+      }
+    )
+
     database = QuasselDatabase.Creator.init(application)
 
     setSupportActionBar(toolbar)
@@ -336,6 +353,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
           session.bufferSyncer?.bufferInfo(bufferId)?.also { bufferInfo ->
             val output = mutableListOf<IAliasManager.Command>()
             for (line in text.lineSequence()) {
+              viewModel.addRecentlySentMessage(line)
               session.aliasManager?.processInput(
                 bufferInfo,
                 inputEditor.formattedString,
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageHistoryAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageHistoryAdapter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c88ed9581b1761009e0a9502f04f8c0f83541a70
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageHistoryAdapter.kt
@@ -0,0 +1,83 @@
+package de.kuschku.quasseldroid_ng.ui.chat
+
+import android.arch.lifecycle.LifecycleOwner
+import android.arch.lifecycle.LiveData
+import android.arch.lifecycle.Observer
+import android.support.v7.util.DiffUtil
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.quasseldroid_ng.R
+
+class MessageHistoryAdapter(
+  lifecycleOwner: LifecycleOwner,
+  liveData: LiveData<List<CharSequence>?>,
+  runInBackground: (() -> Unit) -> Any,
+  runOnUiThread: (Runnable) -> Any,
+  private val clickListener: ((CharSequence) -> Unit)? = null
+) : RecyclerView.Adapter<MessageHistoryAdapter.MessageViewHolder>() {
+  var data = mutableListOf<CharSequence>()
+
+  init {
+    liveData.observe(lifecycleOwner, Observer { it: List<CharSequence>? ->
+      runInBackground {
+        val list = it ?: emptyList()
+        val old: List<CharSequence> = data
+        val new: List<CharSequence> = list
+        val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+          override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
+            old[oldItemPosition] == new[newItemPosition]
+
+          override fun getOldListSize() = old.size
+          override fun getNewListSize() = new.size
+          override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
+            old[oldItemPosition] == new[newItemPosition]
+        }, true)
+        runOnUiThread(Runnable {
+          data.clear()
+          data.addAll(new)
+          result.dispatchUpdatesTo(this@MessageHistoryAdapter)
+        })
+      }
+    })
+  }
+
+  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MessageViewHolder(
+    LayoutInflater.from(parent.context).inflate(R.layout.widget_history_message, parent, false),
+    clickListener = clickListener
+  )
+
+  override fun onBindViewHolder(holder: MessageViewHolder, position: Int) =
+    holder.bind(data[position])
+
+  override fun getItemCount() = data.size
+
+  class MessageViewHolder(
+    itemView: View,
+    private val clickListener: ((CharSequence) -> Unit)? = null
+  ) : RecyclerView.ViewHolder(itemView) {
+    @BindView(R.id.content)
+    lateinit var content: TextView
+
+    var value: CharSequence? = null
+
+    init {
+      ButterKnife.bind(this, itemView)
+      itemView.setOnClickListener {
+        val value = value
+        if (value != null)
+          clickListener?.invoke(value)
+      }
+    }
+
+    fun bind(data: CharSequence) {
+      value = data
+
+      content.text = data
+    }
+  }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt
index faf2619f0c116f727e2a5c17c7093d2d562cee71..32baa2206aff31d10ef24c3b8336a9d12c4621f9 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt
@@ -43,6 +43,16 @@ class QuasselViewModel : ViewModel() {
     this.bufferViewConfig.value = bufferViewConfig
   }
 
+  val MAX_RECENT_MESSAGES = 20
+  val recentlySentMessages = MutableLiveData<List<CharSequence>>()
+  fun addRecentlySentMessage(message: CharSequence) {
+    recentlySentMessages.value =
+      listOf(message) +
+      recentlySentMessages.value.orEmpty()
+        .filter { it == message }
+        .take(MAX_RECENT_MESSAGES - 1)
+  }
+
   val backend = backendWrapper.switchMap { it }
   val sessionManager = backend.map { it.sessionManager() }
   val session = sessionManager.switchMapRx { it.session }
@@ -371,92 +381,92 @@ class QuasselViewModel : ViewModel() {
             ids.mapNotNull { id ->
               bufferSyncer.bufferInfo(id)
             }.filter {
-                currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId
-              }.filter {
-                (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() ||
-                it.type.hasFlag(Buffer_Type.StatusBuffer)
-              }.mapNotNull {
-                val network = session.networks[it.networkId]
-                if (network == null) {
-                  null
-                } else {
-                  it to network
+              currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId
+            }.filter {
+              (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() ||
+              it.type.hasFlag(Buffer_Type.StatusBuffer)
+            }.mapNotNull {
+              val network = session.networks[it.networkId]
+              if (network == null) {
+                null
+              } else {
+                it to network
+              }
+            }.map<Pair<BufferInfo, Network>, Observable<BufferListAdapter.BufferProps>?> { (info, network) ->
+              bufferSyncer.liveActivity(info.bufferId).switchMap { activity ->
+                bufferSyncer.liveHighlightCount(info.bufferId).map { highlights ->
+                  activity to highlights
                 }
-              }.map<Pair<BufferInfo, Network>, Observable<BufferListAdapter.BufferProps>?> { (info, network) ->
-                bufferSyncer.liveActivity(info.bufferId).switchMap { activity ->
-                  bufferSyncer.liveHighlightCount(info.bufferId).map { highlights ->
-                    activity to highlights
-                  }
-                }.switchMap { (activity, highlights) ->
-                    when (info.type.toInt()) {
-                      BufferInfo.Type.QueryBuffer.toInt()   -> {
-                        network.liveIrcUser(info.bufferName).switchMap { user ->
-                          user.liveIsAway().switchMap { away ->
-                            user.liveRealName().map { realName ->
-                              BufferListAdapter.BufferProps(
-                                info = info,
-                                network = network.networkInfo(),
-                                bufferStatus = when {
-                                  user == IrcUser.NULL -> BufferListAdapter.BufferStatus.OFFLINE
-                                  away                 -> BufferListAdapter.BufferStatus.AWAY
-                                  else                 -> BufferListAdapter.BufferStatus.ONLINE
-                                },
-                                description = realName,
-                                activity = activity,
-                                highlights = highlights,
-                                hiddenState = state
-                              )
-                            }
-                          }
-                        }
-                      }
-                      BufferInfo.Type.ChannelBuffer.toInt() -> {
-                        network.liveIrcChannel(
-                          info.bufferName
-                        ).switchMap { channel ->
-                          channel.liveTopic().map { topic ->
-                            BufferListAdapter.BufferProps(
-                              info = info,
-                              network = network.networkInfo(),
-                              bufferStatus = when (channel) {
-                                IrcChannel.NULL -> BufferListAdapter.BufferStatus.OFFLINE
-                                else            -> BufferListAdapter.BufferStatus.ONLINE
-                              },
-                              description = topic,
-                              activity = activity,
-                              highlights = highlights,
-                              hiddenState = state
-                            )
-                          }
-                        }
-                      }
-                      BufferInfo.Type.StatusBuffer.toInt()  -> {
-                        network.liveConnectionState.map {
+              }.switchMap { (activity, highlights) ->
+                when (info.type.toInt()) {
+                  BufferInfo.Type.QueryBuffer.toInt()   -> {
+                    network.liveIrcUser(info.bufferName).switchMap { user ->
+                      user.liveIsAway().switchMap { away ->
+                        user.liveRealName().map { realName ->
                           BufferListAdapter.BufferProps(
                             info = info,
                             network = network.networkInfo(),
-                            bufferStatus = BufferListAdapter.BufferStatus.OFFLINE,
-                            description = "",
+                            bufferStatus = when {
+                              user == IrcUser.NULL -> BufferListAdapter.BufferStatus.OFFLINE
+                              away                 -> BufferListAdapter.BufferStatus.AWAY
+                              else                 -> BufferListAdapter.BufferStatus.ONLINE
+                            },
+                            description = realName,
                             activity = activity,
                             highlights = highlights,
                             hiddenState = state
                           )
                         }
                       }
-                      else                                  -> Observable.just(
+                    }
+                  }
+                  BufferInfo.Type.ChannelBuffer.toInt() -> {
+                    network.liveIrcChannel(
+                      info.bufferName
+                    ).switchMap { channel ->
+                      channel.liveTopic().map { topic ->
                         BufferListAdapter.BufferProps(
                           info = info,
                           network = network.networkInfo(),
-                          bufferStatus = BufferListAdapter.BufferStatus.OFFLINE,
-                          description = "",
+                          bufferStatus = when (channel) {
+                            IrcChannel.NULL -> BufferListAdapter.BufferStatus.OFFLINE
+                            else            -> BufferListAdapter.BufferStatus.ONLINE
+                          },
+                          description = topic,
                           activity = activity,
                           highlights = highlights,
                           hiddenState = state
                         )
+                      }
+                    }
+                  }
+                  BufferInfo.Type.StatusBuffer.toInt()  -> {
+                    network.liveConnectionState.map {
+                      BufferListAdapter.BufferProps(
+                        info = info,
+                        network = network.networkInfo(),
+                        bufferStatus = BufferListAdapter.BufferStatus.OFFLINE,
+                        description = "",
+                        activity = activity,
+                        highlights = highlights,
+                        hiddenState = state
                       )
                     }
                   }
+                  else                                  -> Observable.just(
+                    BufferListAdapter.BufferProps(
+                      info = info,
+                      network = network.networkInfo(),
+                      bufferStatus = BufferListAdapter.BufferStatus.OFFLINE,
+                      description = "",
+                      activity = activity,
+                      highlights = highlights,
+                      hiddenState = state
+                    )
+                  )
+                }
               }
+            }
 
           bufferSyncer.liveBufferInfos().switchMap {
             val buffers = if (showHidden) {
@@ -490,5 +500,6 @@ class QuasselViewModel : ViewModel() {
     showHidden.postValue(false)
     selectedBufferId.postValue(-1)
     collapsedNetworks.value = emptySet()
+    recentlySentMessages.value = emptyList()
   }
 }
diff --git a/app/src/main/res/layout/widget_history_message.xml b/app/src/main/res/layout/widget_history_message.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c0df0098cea2206c6fed236a8b54461b1c28d211
--- /dev/null
+++ b/app/src/main/res/layout/widget_history_message.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:tools="http://schemas.android.com/tools"
+  android:id="@+id/content"
+  android:layout_width="match_parent"
+  android:layout_height="wrap_content"
+  android:layout_gravity="center"
+  android:background="?attr/backgroundMenuItem"
+  android:fontFamily="sans-serif-medium"
+  android:gravity="center_vertical"
+  android:minHeight="48dp"
+  android:paddingBottom="8dp"
+  android:paddingLeft="16dp"
+  android:paddingRight="16dp"
+  android:paddingTop="8dp"
+  android:singleLine="true"
+  android:textColor="?attr/colorTextPrimary"
+  android:textSize="13sp"
+  tools:text="Historical Message" />
\ No newline at end of file