diff --git a/app/components/viewer/document/index.hbs b/app/components/viewer/document/index.hbs
index b887ef330f66a0a6e64ddfd235a746d636b35c6c..3f6e66c0134b5ce718eee2b74c687f60aa53b8d7 100644
--- a/app/components/viewer/document/index.hbs
+++ b/app/components/viewer/document/index.hbs
@@ -7,6 +7,8 @@
       @onDblClick={{this.onThumbnailDblClick}}
       @onCheckboxChange={{@onCheckboxChange}}
       @onThumbnailsPositionChanged={{@onThumbnailsPositionChanged}}
+      @onAddThumbnailPlaceholderAt={{@onAddThumbnailPlaceholderAt}}
+      @onRemoveThumbnailPlaceholder={{@onRemoveThumbnailPlaceholder}}
       @onIncomingPages={{@onIncomingPages}} />
   {{/if}}
   <Viewer::ThumbnailsSwitch
diff --git a/app/components/viewer/index.hbs b/app/components/viewer/index.hbs
index 693d352c88fd8c9e992c00f7586886d9620790cd..1925961bb7d809f71d10d11cc373c90fb5b96709 100644
--- a/app/components/viewer/index.hbs
+++ b/app/components/viewer/index.hbs
@@ -53,5 +53,7 @@
     @thumbnails_visible={{this.thumbnails_visible}}
     @onCheckboxChange={{this.onThumbnailCheckboxChange}}
     @onThumbnailsPositionChanged={{this.onThumbnailsPositionChanged}}
+    @onAddThumbnailPlaceholderAt={{this.onAddThumbnailPlaceholderAt}}
+    @onRemoveThumbnailPlaceholder={{this.onRemoveThumbnailPlaceholder}}
     @onIncomingPages={{this.onIncomingPages}} />
 </div>
diff --git a/app/components/viewer/index.js b/app/components/viewer/index.js
index 4066871d8382112a82c93c3b0c3dd3ab2eed459f..3afae65deb38ea79b212693c65b9f392ee01e9b1 100644
--- a/app/components/viewer/index.js
+++ b/app/components/viewer/index.js
@@ -5,7 +5,6 @@ import { action } from '@ember/object';
 import { A } from '@ember/array';
 import {
   reposition_items,
-  get_id,
   detect_order_changes
 } from 'papermerge/utils/array';
 
@@ -153,16 +152,64 @@ export default class ViewerComponent extends Component {
   }
 
   @action
-  onThumbnailsPositionChanged({original_pos, drop_pos, page_ids}) {
-    let all_pages = this.pages;
+  onThumbnailsPositionChanged(page_ids) {
+    /*
+    ``page_ids`` will be moved to the new position
+    indicated by drop placeholder.
+    Page is drop placeholder if ``page.is_drop_placeholder`` is true.
+    */
+    let pages_without_placeholder,
+      drop_placeholder_pos;
+
+    // learn where user wants to move pages by
+    // findind drop placeholder position
+    drop_placeholder_pos = this.pages.findIndex(
+      item => item.is_drop_placeholder
+    );
+
+    // remove placeholder from pages array
+    this.pages.splice(drop_placeholder_pos, 1);
+    pages_without_placeholder = this.pages;
 
+    console.log(`repositioning items page_ids=${page_ids}`);
+    console.log(`repositioning items drop_pos=${drop_placeholder_pos}`);
+    // reposition pages
     this.pages = reposition_items({
-      items: all_pages,
+      items: pages_without_placeholder,
       selected_ids: page_ids,
-      drop_pos: drop_pos
+      drop_pos: drop_placeholder_pos
     });
   }
 
+  @action
+  onAddThumbnailPlaceholderAt(pos) {
+    let new_pages,
+      drop_placeholder;
+
+    new_pages = Array.from(this.pages);
+    drop_placeholder = {'is_drop_placeholder': true};
+
+    if (!new_pages.find(item => item.is_drop_placeholder)) {
+      // Only one drop placeholder is allowed
+      new_pages.splice(pos, 0, drop_placeholder);
+      this.pages = new_pages;
+    }
+  }
+
+  @action
+  onRemoveThumbnailPlaceholder() {
+    let new_pages,
+      drop_placeholder_pos;
+
+    new_pages = Array.from(this.pages);
+    drop_placeholder_pos = new_pages.findIndex(item => item.is_drop_placeholder);
+
+    if (drop_placeholder_pos >= 0) {
+      new_pages.splice(drop_placeholder_pos, 1);
+      this.pages = new_pages;
+    }
+  }
+
   @action
   async onIncomingPages({page_ids, drop_pos}) {
     await this.requests.moveToDocument({
diff --git a/app/components/viewer/pages.hbs b/app/components/viewer/pages.hbs
index 8c6e3616228753cb1328123a33798243f8b2efb4..59746e7fc377fcc7080dd74d32a7f4f99c31880a 100644
--- a/app/components/viewer/pages.hbs
+++ b/app/components/viewer/pages.hbs
@@ -1,10 +1,14 @@
 <div class="d-flex flex-column pages"
   {{adjust_element_height}}>
     {{#each @pages as |page|}}
-      <Viewer::Page
-        @page={{page}}
-        @scroll_to_page={{@scroll_to_page}}
-        @zoom_factor={{@zoom_factor}} />
+      {{#if page.is_drop_placeholder}}
+        <!-- no op -->
+      {{else}}
+        <Viewer::Page
+          @page={{page}}
+          @scroll_to_page={{@scroll_to_page}}
+          @zoom_factor={{@zoom_factor}} />
+      {{/if}}
     {{/each}}
     <Viewer::Zoom
       @onZoomIn={{@onZoomIn}}
diff --git a/app/components/viewer/thumbnail/drop_placeholder.hbs b/app/components/viewer/thumbnail/drop_placeholder.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..479f4a726c2a31f6a83b96e8458e35f6e555fb42
--- /dev/null
+++ b/app/components/viewer/thumbnail/drop_placeholder.hbs
@@ -0,0 +1,2 @@
+<div class='drop-placeholder thumbnail d-flex flex-column align-items-center px-2'>
+</div>
diff --git a/app/components/viewer/thumbnail/index.js b/app/components/viewer/thumbnail/index.js
index 268c59a982a006d158d6835e8ba03e6a9d2fdbbf..efaec2e5a2ed6db965e176d41ad720713482b58c 100644
--- a/app/components/viewer/thumbnail/index.js
+++ b/app/components/viewer/thumbnail/index.js
@@ -31,9 +31,12 @@ export default class ViewerThumbnailComponent extends Component {
       pages: items,
       page: model,
       original_pos: original_pos,
-      source_doc_id: this.args.doc.id
+      source_doc_id: this.args.doc.id,
+      element: element
     };
 
+    element.classList.add('is-being-dragged');
+
     event.dataTransfer.setData(
       'application/x.page',
       JSON.stringify(data)
diff --git a/app/components/viewer/thumbnails/index.hbs b/app/components/viewer/thumbnails/index.hbs
index a5d37e3691be78985db3202833a9cc667093367a..c28be67f3386586bafa16cecb8b8e4ac8d13ab0d 100644
--- a/app/components/viewer/thumbnails/index.hbs
+++ b/app/components/viewer/thumbnails/index.hbs
@@ -8,13 +8,17 @@
 
   {{adjust_element_height}}>
   {{#each @pages as |page|}}
-    <Viewer::Thumbnail
-      @doc={{@doc}}
-      @page={{page}}
-      @selectedPages={{@selectedPages}}
-      @onDblClick={{@onDblClick}}
-      @onDragendSuccess={{this.onDragendSuccess}}
-      @onDragendCancel={{this.onDragendCancel}}
-      @onCheckboxChange={{@onCheckboxChange}} />
+    {{#if page.is_drop_placeholder}}
+      <Viewer::Thumbnail::DropPlaceholder />
+    {{else}}
+      <Viewer::Thumbnail
+        @doc={{@doc}}
+        @page={{page}}
+        @selectedPages={{@selectedPages}}
+        @onDblClick={{@onDblClick}}
+        @onDragendSuccess={{this.onDragendSuccess}}
+        @onDragendCancel={{this.onDragendCancel}}
+        @onCheckboxChange={{@onCheckboxChange}} />
+    {{/if}}
   {{/each}}
 </div>
diff --git a/app/components/viewer/thumbnails/index.js b/app/components/viewer/thumbnails/index.js
index 10729f555557f7e762031990469546d098ac3836..48b4bc37fd70d7c883fc663e3054cd287eab59ac 100644
--- a/app/components/viewer/thumbnails/index.js
+++ b/app/components/viewer/thumbnails/index.js
@@ -1,17 +1,18 @@
 import Component from '@glimmer/component';
 import Point from 'papermerge/utils/point';
 import { action } from '@ember/object';
-import { get_cursor_pos_within_element } from 'papermerge/utils/dom';
 
 
 export default class ViewerThumbnailsComponent extends Component {
 
   @action
-  onDragendCancel() {
+  onDragendCancel({event, element}) {
+    element.classList.remove('is-being-dragged');
   }
 
   @action
-  onDragendSuccess() {
+  onDragendSuccess({event, element}) {
+    element.classList.remove('is-being-dragged');
   }
 
   @action
@@ -19,32 +20,23 @@ export default class ViewerThumbnailsComponent extends Component {
     let data,
       json_data,
       page_ids,
-      original_pos,
       drop_pos,
       source_doc_id;
 
-    event.preventDefault();
     data = event.dataTransfer.getData('application/x.page');
+
     if (!data) {
       console.warn('Accepts only application/x.page data');
       return;
     }
     json_data = JSON.parse(data);
 
-    original_pos = json_data['original_pos']
     page_ids = json_data['pages'].map(page => page.id);
     source_doc_id = json_data['source_doc_id'];
 
-    drop_pos = get_cursor_pos_within_element(
-      element,
-      new Point(event.clientX, event.clientY)
-    );
-
     if (source_doc_id == this.args.doc.id) {
       // pages moved within same document
-      this.args.onThumbnailsPositionChanged({
-        original_pos, drop_pos, page_ids
-      });
+      this.args.onThumbnailsPositionChanged(page_ids);
     } else {
       // pages moved to another document
       this.args.onIncomingPages({
@@ -54,7 +46,78 @@ export default class ViewerThumbnailsComponent extends Component {
   }
 
   @action
-  onDragOver() {
+  onDragOver({event, element}) {
+    /*
+    Creates DOM placeholder suggesting to user that here he/she can drop the page.
+
+    Only one placeholder DOM element is allowed.
+    */
+    let thumbnail_dom_items,
+      cursor_coord,
+      suggested_pos,
+      rect,
+      cursor_before_child = 0,
+      outside_all_thumbnails = true,
+      svg_element,
+      data,
+      json_data,
+      original_pos;
+
+    if (!element) {
+      return;
+    }
+
+    data = event.dataTransfer.getData('application/x.page');
+    if (!data) {
+      console.warn('Accepts only application/x.page data');
+      return;
+    }
+    json_data = JSON.parse(data);
+
+    original_pos = json_data['original_pos']
+
+    cursor_coord = new Point(event.clientX, event.clientY);
+    thumbnail_dom_items = Array.from(element.children);
+
+    thumbnail_dom_items.forEach(thumbnail_dom_item => {
+      // page_item is DOM element which may be real thumbnail of the page or
+      // it may be a paceholder used as suggestion that it is OK to drop page here.
+      // Real page thumbnail DOM element contains DOM element for image/svg
+      // and DOM element denoting page number
+      svg_element = thumbnail_dom_item.querySelector('svg');
+      if (svg_element) { // in case of thumbnail placeholder, there won't be SVG element
+        rect = svg_element.getBoundingClientRect();
+
+        if (cursor_coord.y <= rect.bottom || cursor_coord.y <= rect.top) {
+          cursor_before_child += 1;
+        }
+        // Check if cursor position is outside of any thumbnail i.e.
+        // position to drop will be suggested only in case cursor coordinate
+        // is BETWEEN thumbnails images/svg
+        if ((cursor_coord.y < rect.bottom) && (cursor_coord.y > rect.top)) {
+          outside_all_thumbnails = false;
+        }
+      }
+    });
+
+    // position where to suggest page drop
+    if (element.querySelector('.drop-placeholder')) {
+      suggested_pos = thumbnail_dom_items.length - cursor_before_child - 1;
+    } else {
+      suggested_pos = thumbnail_dom_items.length - cursor_before_child;
+    }
+
+    if (outside_all_thumbnails) {
+      // suggest position to drop ONLY if cursor is outside of all thumbnails
+      console.log(`suggested_pos=${suggested_pos}; original_pos=${original_pos}`);
+      if (Math.abs(original_pos - suggested_pos) >= 1 && suggested_pos != original_pos + 1) {
+        // prevent default to allow drop
+        event.preventDefault();
+        this.args.onAddThumbnailPlaceholderAt(suggested_pos);
+      }
+    } else {
+      this.args.onRemoveThumbnailPlaceholder();
+    }
   }
 
   @action
diff --git a/app/modifiers/draggable.js b/app/modifiers/draggable.js
index b35828a23a135a12f8e094ca493b17e891c5052a..3bbfb3887be14a8296d39fa4f9455a22e7007c6c 100644
--- a/app/modifiers/draggable.js
+++ b/app/modifiers/draggable.js
@@ -112,9 +112,19 @@ export default class DraggableModifier extends Modifier {
     const ondragend_cancel = this.args.named['onDragendCancel'];
 
     if (event.dataTransfer.dropEffect === "move") {
-      ondragend_success(this.model, this.selected_items);
+      ondragend_success({
+        event: this.event,
+        element: this.element,
+        model: this.model,
+        selected_items: this.selected_items
+      });
     } else {
-      ondragend_cancel(this.model, this.selected_items);
+      ondragend_cancel({
+        event: this.event,
+        element: this.element,
+        model: this.model,
+        selected_items: this.selected_items
+      });
     }
   }
 }
diff --git a/app/modifiers/droppable.js b/app/modifiers/droppable.js
index 32577290db2a8629905ccc29484334badf75c5c6..1822f8b6019aa21ad8619c2ed3cc6b38ec0015fb 100644
--- a/app/modifiers/droppable.js
+++ b/app/modifiers/droppable.js
@@ -38,11 +38,17 @@ export default class DrappableModifier extends Modifier {
 
   @action
   onDragOver(event) {
-    const isNode = event.dataTransfer.types.includes("application/x.node");
+    //const isNode = event.dataTransfer.types.includes("application/x.node");
 
-    event.preventDefault();
-    if (isNode) {
+    //event.preventDefault();
+    //if (isNode) {
       //console.log(`dragging over a node`);
+    //}
+    let _onDragOver = this.args.named['onDragOver'], element;
+
+    element = this.element;
+    if (_onDragOver) {
+      _onDragOver({event, element});
     }
   }
 
diff --git a/app/styles/document_version.scss b/app/styles/document_version.scss
index d1baf3eff65c06ce5927f36dbb548f0eb7455342..ee5f580c56971c9ba4442ff8433a540f61de4d87 100644
--- a/app/styles/document_version.scss
+++ b/app/styles/document_version.scss
@@ -30,6 +30,16 @@
       outline: 1px solid #A6DAFF;
     }
   }
+
+  .drop-placeholder {
+    margin: 0.85rem;
+    min-height: 7rem;
+    border: 1px dashed #aaa;
+  }
+
+  .is-being-dragged {
+    opacity: 0.5;
+  }
 }
 
 .thumbnails-switch {
diff --git a/app/utils/array.js b/app/utils/array.js
index 46b92d115b0f28062144cc5258b20781ff5fac73..67bcecce12294ed515cc4c6aac77b6f5f70bc38a 100644
--- a/app/utils/array.js
+++ b/app/utils/array.js
@@ -124,7 +124,7 @@ function detect_order_changes(arr1, arr2) {
   }
 
   if (arr1.length != arr2.length) {
-    throw 'Invalid input. Both arrays need to be of same length';
+    return false;
   }
 
   arr1.forEach((item, index) => {