diff --git a/platypush/backend/http/webapp/src/components/elements/ConfirmDialog.vue b/platypush/backend/http/webapp/src/components/elements/ConfirmDialog.vue
index b1c82d2f8..ad9329985 100644
--- a/platypush/backend/http/webapp/src/components/elements/ConfirmDialog.vue
+++ b/platypush/backend/http/webapp/src/components/elements/ConfirmDialog.vue
@@ -43,13 +43,21 @@ export default {
       this.close()
     },
 
-    show() {
+    open() {
       this.$refs.modal.show()
     },
 
     close() {
       this.$refs.modal.hide()
     },
+
+    show() {
+      this.open()
+    },
+
+    hide() {
+      this.close()
+    },
   },
 }
 </script>
diff --git a/platypush/backend/http/webapp/src/components/elements/TextPrompt.vue b/platypush/backend/http/webapp/src/components/elements/TextPrompt.vue
index e68aee55b..3c0f5aa75 100644
--- a/platypush/backend/http/webapp/src/components/elements/TextPrompt.vue
+++ b/platypush/backend/http/webapp/src/components/elements/TextPrompt.vue
@@ -3,7 +3,7 @@
     <form @submit.prevent="onConfirm">
       <div class="dialog-content">
         <slot />
-        <input type="text" ref="input" />
+        <input type="text" ref="input" v-model="value_" />
       </div>
 
       <div class="buttons">
@@ -38,26 +38,79 @@ export default {
       type: String,
       default: "Cancel",
     },
+
+    visible: {
+      type: Boolean,
+      default: false,
+    },
+
+    value: {
+      type: String,
+      default: "",
+    },
+  },
+
+  data() {
+    return {
+      value_: "",
+      visible_: false,
+    }
   },
 
   methods: {
     onConfirm() {
-      this.$emit('input', this.$refs.input.value)
+      this.$emit('input', this.value_)
       this.close()
     },
 
-    show() {
+    open() {
+      if (this.visible_)
+        return
+
+      this.value_ = this.value
       this.$refs.modal.show()
+      this.visible_ = true
+      this.focus()
     },
 
     close() {
+      if (!this.visible_)
+        return
+
+      this.value_ = ""
       this.$refs.modal.hide()
+      this.visible_ = false
+    },
+
+    show() {
+      this.open()
+    },
+
+    hide() {
+      this.close()
+    },
+
+    focus() {
+      this.$nextTick(() => {
+        this.$refs.input.focus()
+      })
+    },
+  },
+
+  watch: {
+    visible(val) {
+      if (val) {
+        this.open()
+      } else {
+        this.close()
+      }
     },
   },
 
   mounted() {
+    this.visible_ = this.visible
+    this.value_ = this.value || ""
     this.$nextTick(() => {
-      this.$refs.input.value = ""
       this.$refs.input.focus()
     })
   },
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Browser.vue b/platypush/backend/http/webapp/src/components/panels/Media/Browser.vue
index 5244b21ec..291cc989e 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Browser.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Browser.vue
@@ -23,9 +23,11 @@
         <component
             :is="mediaProvider"
             :filter="filter"
+            @add-to-playlist="$emit('add-to-playlist', $event)"
             @back="mediaProvider = null"
             @path-change="$emit('path-change', $event)"
-            @play="$emit('play', $event)" />
+            @play="$emit('play', $event)"
+        />
       </div>
     </div>
   </keep-alive>
@@ -39,8 +41,17 @@ import Utils from "@/Utils";
 import providersMetadata from "./Providers/meta.json";
 
 export default {
-  emits: ['path-change', 'play'],
   mixins: [Utils],
+  emits: [
+    'add-to-playlist',
+    'create-playlist',
+    'path-change',
+    'play',
+    'remove-from-playlist',
+    'remove-playlist',
+    'rename-playlist',
+  ],
+
   components: {
     Browser,
     Loading,
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Index.vue b/platypush/backend/http/webapp/src/components/panels/Media/Index.vue
index 311d3bb18..364a330af 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Index.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Index.vue
@@ -39,6 +39,7 @@
                        :plugin-name="pluginName"
                        :loading="loading"
                        :filter="browserFilter"
+                       @add-to-playlist="addToPlaylistItem = $event"
                        @select="onResultSelect($event)"
                        @play="play"
                        @view="view"
@@ -51,6 +52,7 @@
                            v-else-if="selectedView === 'torrents'" />
 
               <Browser :filter="browserFilter"
+                       @add-to-playlist="addToPlaylistItem = $event"
                        @path-change="browserFilter = ''"
                        @play="play($event)"
                        v-else-if="selectedView === 'browser'" />
@@ -75,6 +77,16 @@
           <UrlPlayer :value="urlPlay" @input="urlPlay = $event.target.value" @play="playUrl($event)" />
         </Modal>
       </div>
+
+      <div class="add-to-playlist-container" v-if="addToPlaylistItem">
+        <Modal title="Add to playlist" :visible="addToPlaylistItem != null" @close="addToPlaylistItem = null">
+          <PlaylistAdder
+            :item="addToPlaylistItem"
+            @done="addToPlaylistItem = null"
+            @close="addToPlaylistItem = null"
+          />
+        </Modal>
+      </div>
     </div>
   </keep-alive>
 </template>
@@ -88,6 +100,7 @@ import Header from "@/components/panels/Media/Header";
 import MediaUtils from "@/components/Media/Utils";
 import MediaView from "@/components/Media/View";
 import Nav from "@/components/panels/Media/Nav";
+import PlaylistAdder from "@/components/panels/Media/PlaylistAdder";
 import Results from "@/components/panels/Media/Results";
 import Subtitles from "@/components/panels/Media/Subtitles";
 import Transfers from "@/components/panels/Torrent/Transfers";
@@ -102,6 +115,7 @@ export default {
     MediaView,
     Modal,
     Nav,
+    PlaylistAdder,
     Results,
     Subtitles,
     Transfers,
@@ -139,6 +153,7 @@ export default {
       awaitingPlayTorrent: null,
       urlPlay: null,
       browserFilter: null,
+      addToPlaylistItem: null,
       torrentPlugin: null,
       torrentPlugins: [
         'torrent',
@@ -487,4 +502,10 @@ export default {
     }
   }
 }
+
+:deep(.add-to-playlist-container) {
+  .body {
+    padding: 0 !important;
+  }
+}
 </style>
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Item.vue b/platypush/backend/http/webapp/src/components/panels/Media/Item.vue
index 902f46cae..0ffceb3e6 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Item.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Item.vue
@@ -4,6 +4,7 @@
     :class="{selected: selected}"
     @click.right.prevent="$refs.dropdown.toggle()"
     v-if="!hidden">
+
     <div class="thumbnail">
       <MediaImage :item="item" @play="$emit('play')" />
     </div>
@@ -19,6 +20,10 @@
                           v-if="item.type === 'torrent'" />
             <DropdownItem icon-class="fa fa-window-maximize" text="View in browser" @click="$emit('view')"
                           v-if="item.type === 'file'" />
+            <DropdownItem icon-class="fa fa-list" text="Add to playlist" @click="$emit('add-to-playlist')"
+                          v-if="item.type === 'youtube'" />
+            <DropdownItem icon-class="fa fa-trash" text="Remove from playlist" @click="$refs.confirmPlaylistRemove.open()"
+                          v-if="playlist" />
             <DropdownItem icon-class="fa fa-info-circle" text="Info" @click="$emit('select')" />
           </Dropdown>
         </div>
@@ -35,10 +40,15 @@
         {{ formatDateTime(item.created_at, true) }}
       </div>
     </div>
+
+    <ConfirmDialog ref="confirmPlaylistRemove" @input="$emit('remove-from-playlist')">
+      Are you sure you want to remove this item from the playlist?
+    </ConfirmDialog>
   </div>
 </template>
 
 <script>
+import ConfirmDialog from "@/components/elements/ConfirmDialog";
 import Dropdown from "@/components/elements/Dropdown";
 import DropdownItem from "@/components/elements/DropdownItem";
 import Icons from "./icons.json";
@@ -46,9 +56,23 @@ import MediaImage from "./MediaImage";
 import Utils from "@/Utils";
 
 export default {
-  components: {Dropdown, DropdownItem, MediaImage},
   mixins: [Utils],
-  emits: ['play', 'select', 'view', 'download'],
+  components: {
+    ConfirmDialog,
+    Dropdown,
+    DropdownItem,
+    MediaImage,
+  },
+
+  emits: [
+    'add-to-playlist',
+    'download',
+    'play',
+    'remove-from-playlist',
+    'select',
+    'view',
+  ],
+
   props: {
     item: {
       type: Object,
@@ -64,6 +88,10 @@ export default {
       type: Boolean,
       default: false,
     },
+
+    playlist: {
+      default: null,
+    },
   },
 
   data() {
@@ -87,6 +115,10 @@ export default {
   border: 1px solid transparent;
   border-bottom: 1px solid transparent !important;
 
+  @include from($tablet) {
+    max-height: max(25em, 25%);
+  }
+
   &.selected {
     box-shadow: $border-shadow-bottom;
     background: $selected-bg;
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/PlaylistAdder.vue b/platypush/backend/http/webapp/src/components/panels/Media/PlaylistAdder.vue
new file mode 100644
index 000000000..1bf1a25d2
--- /dev/null
+++ b/platypush/backend/http/webapp/src/components/panels/Media/PlaylistAdder.vue
@@ -0,0 +1,162 @@
+<template>
+  <div class="playlist-adder-container">
+    <Loading v-if="loading" />
+    <TextPrompt ref="newPlaylistName" :visible="showNewPlaylist" @input="createPlaylist($event)">
+      Playlist name
+    </TextPrompt>
+
+    <div class="playlists">
+      <div class="playlist new-playlist">
+        <button @click="showNewPlaylist = true">
+          <i class="fa fa-plus" />
+          Create new playlist
+        </button>
+      </div>
+
+      <div class="playlist" v-for="playlist in playlists" :key="playlist.id">
+        <button @click="addToPlaylist(playlist.id)">
+          <i class="fa fa-list" />
+          {{ playlist.name }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Loading from "@/components/Loading";
+import Utils from "@/Utils";
+import TextPrompt from "@/components/elements/TextPrompt"
+
+export default {
+  emits: ['done'],
+  mixins: [Utils],
+  components: {Loading, TextPrompt},
+  props: {
+    item: {
+      type: Object,
+      required: true,
+    },
+  },
+
+  data() {
+    return {
+      loading: false,
+      playlists: [],
+      showNewPlaylist: false,
+    }
+  },
+
+  methods: {
+    async createPlaylist(name) {
+      name = name?.trim()
+      if (!name?.length)
+        return
+
+      this.loading = true
+
+      try {
+        const playlist = await this.request('youtube.create_playlist', {
+          name: name,
+        })
+
+        await this.request('youtube.add_to_playlist', {
+          playlist_id: playlist.id,
+          video_id: this.item.id || this.item.url,
+        })
+
+        this.$emit('done')
+        this.notify({
+          text: 'Playlist created and video added',
+          image: {
+            icon: 'check',
+          }
+        })
+
+      } finally {
+        this.loading = false
+        this.showNewPlaylist = false
+      }
+    },
+
+    async refreshPlaylists() {
+      this.loading = true
+
+      try {
+        this.playlists = await this.request('youtube.get_playlists')
+      } finally {
+        this.loading = false
+      }
+    },
+
+    async addToPlaylist(playlistId) {
+      this.loading = true
+
+      try {
+        await this.request('youtube.add_to_playlist', {
+          playlist_id: playlistId,
+          video_id: this.item.id || this.item.url,
+        })
+
+        this.notify({
+          text: 'Video added to playlist',
+          image: {
+            icon: 'check',
+          }
+        })
+
+        this.$emit('done')
+      } finally {
+        this.loading = false
+      }
+    },
+  },
+
+  mounted() {
+    this.refreshPlaylists()
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.playlist-adder-container {
+  min-width: 300px;
+  height: 100%;
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  .playlists {
+    width: 100%;
+    overflow-y: auto;
+  }
+
+  .playlist {
+    button {
+      width: 100%;
+      text-align: left;
+      padding: 0.5em 1em;
+      border: none;
+      background: none;
+      cursor: pointer;
+      transition: background 0.2s, color 0.2s;
+
+      &:hover {
+        background: $hover-bg;
+      }
+
+      i {
+        margin-right: 0.5em;
+      }
+    }
+  }
+
+  .new-playlist {
+    button {
+      font-weight: bold;
+      border-bottom: $default-border;
+    }
+  }
+}
+</style>
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Providers/Mixin.vue b/platypush/backend/http/webapp/src/components/panels/Media/Providers/Mixin.vue
index 8de115bc2..4d0181901 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Providers/Mixin.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Providers/Mixin.vue
@@ -2,8 +2,18 @@
 import Utils from "@/Utils";
 
 export default {
-  emits: ['back', 'path-change', 'play'],
   mixins: [Utils],
+  emits: [
+    'add-to-playlist',
+    'back',
+    'create-playlist',
+    'path-change',
+    'play',
+    'remove-from-playlist',
+    'remove-playlist',
+    'rename-playlist',
+  ],
+
   props: {
     filter: {
       type: String,
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube.vue b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube.vue
index c6491590d..f2907cec4 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube.vue
@@ -8,17 +8,27 @@
 
       <div class="body" v-else>
         <Feed :filter="filter"
-              @play="$emit('play', $event)" v-if="selectedView === 'feed'" />
+              @add-to-playlist="$emit('add-to-playlist', $event)"
+              @play="$emit('play', $event)"
+              v-if="selectedView === 'feed'"
+        />
+
         <Playlists :filter="filter"
                    :selected-playlist="selectedPlaylist"
+                   @add-to-playlist="$emit('add-to-playlist', $event)"
                    @play="$emit('play', $event)"
+                   @remove-from-playlist="removeFromPlaylist"
                    @select="onPlaylistSelected"
-                   v-else-if="selectedView === 'playlists'" />
+                   v-else-if="selectedView === 'playlists'"
+        />
+
         <Subscriptions :filter="filter"
                        :selected-channel="selectedChannel"
                        @play="$emit('play', $event)"
                        @select="onChannelSelected"
-                       v-else-if="selectedView === 'subscriptions'" />
+                       v-else-if="selectedView === 'subscriptions'"
+        />
+
         <Index @select="selectView" v-else />
       </div>
     </div>
@@ -87,6 +97,21 @@ export default {
       }
     },
 
+    async removeFromPlaylist(event) {
+      const playlistId = event.playlist_id
+      const videoId = event.item.url
+      this.loading = true
+
+      try {
+        await this.request('youtube.remove_from_playlist', {
+          playlist_id: playlistId,
+          video_id: videoId,
+        })
+      } finally {
+        this.loading = false
+      }
+    },
+
     selectView(view) {
       this.selectedView = view
       if (view === 'playlists')
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Feed.vue b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Feed.vue
index eaadfda6c..58793c622 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Feed.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Feed.vue
@@ -9,6 +9,7 @@
              :filter="filter"
              :sources="{'youtube': true}"
              :selected-result="selectedResult"
+             @add-to-playlist="$emit('add-to-playlist', $event)"
              @select="selectedResult = $event"
              @play="$emit('play', $event)"
              v-else />
@@ -22,8 +23,12 @@ import Results from "@/components/panels/Media/Results";
 import Utils from "@/Utils";
 
 export default {
-  emits: ['play'],
   mixins: [Utils],
+  emits: [
+    'add-to-playlist',
+    'play',
+  ],
+
   components: {
     Loading,
     NoItems,
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlist.vue b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlist.vue
index c09d0089a..4a70e42e0 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlist.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlist.vue
@@ -8,9 +8,12 @@
     <Results :results="items"
              :sources="{'youtube': true}"
              :filter="filter"
+             :playlist="id"
              :selected-result="selectedResult"
-             @select="selectedResult = $event"
+             @add-to-playlist="$emit('add-to-playlist', $event)"
              @play="$emit('play', $event)"
+             @remove-from-playlist="$emit('remove-from-playlist', $event)"
+             @select="selectedResult = $event"
              v-else />
   </div>
 </template>
@@ -22,8 +25,13 @@ import Results from "@/components/panels/Media/Results";
 import Utils from "@/Utils";
 
 export default {
-  emits: ['play'],
   mixins: [Utils],
+  emits: [
+    'add-to-playlist',
+    'play',
+    'remove-from-playlist',
+  ],
+
   components: {
     Loading,
     NoItems,
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlists.vue b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlists.vue
index 823e11145..fae4433bf 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlists.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Providers/YouTube/Playlists.vue
@@ -18,7 +18,14 @@
     </div>
 
     <div class="playlist-body" v-else>
-      <Playlist :id="selectedPlaylist" :filter="filter" @play="$emit('play', $event)" />
+      <Playlist
+        :id="selectedPlaylist"
+        :filter="filter"
+        :playlist="playlist"
+        @add-to-playlist="$emit('add-to-playlist', $event)"
+        @remove-from-playlist="$emit('remove-from-playlist', {item: $event, playlist_id: selectedPlaylist})"
+        @play="$emit('play', $event)"
+      />
     </div>
   </div>
 </template>
@@ -31,8 +38,17 @@ import Playlist from "./Playlist";
 import Utils from "@/Utils";
 
 export default {
-  emits: ['play', 'select'],
   mixins: [Utils],
+  emits: [
+    'add-to-playlist',
+    'create-playlist',
+    'play',
+    'remove-from-playlist',
+    'remove-playlist',
+    'rename-playlist',
+    'select',
+  ],
+
   components: {
     Loading,
     MediaImage,
diff --git a/platypush/backend/http/webapp/src/components/panels/Media/Results.vue b/platypush/backend/http/webapp/src/components/panels/Media/Results.vue
index 62f14722b..5d4292f46 100644
--- a/platypush/backend/http/webapp/src/components/panels/Media/Results.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Media/Results.vue
@@ -4,9 +4,12 @@
     <div class="grid" ref="grid" v-if="results?.length" @scroll="onScroll">
       <Item v-for="(item, i) in visibleResults"
             :key="i"
-            :item="item"
-            :selected="selectedResult === i"
             :hidden="!!Object.keys(sources || {}).length && !sources[item.type]"
+            :item="item"
+            :playlist="playlist"
+            :selected="selectedResult === i"
+            @add-to-playlist="$emit('add-to-playlist', item)"
+            @remove-from-playlist="$emit('remove-from-playlist', item)"
             @select="$emit('select', i)"
             @play="$emit('play', item)"
             @view="$emit('view', item)"
@@ -30,7 +33,16 @@ import Modal from "@/components/Modal";
 
 export default {
   components: {Info, Item, Loading, Modal},
-  emits: ['select', 'play', 'view', 'download', 'scroll-end'],
+  emits: [
+    'add-to-playlist',
+    'download',
+    'play',
+    'remove-from-playlist',
+    'scroll-end',
+    'select',
+    'view',
+  ],
+
   props: {
     loading: {
       type: Boolean,
@@ -64,6 +76,10 @@ export default {
       type: Number,
       default: 25,
     },
+
+    playlist: {
+      default: null,
+    },
   },
 
   data() {