diff --git a/client/src/app/instance/search/components/result/datatable.component.html b/client/src/app/instance/search/components/result/datatable.component.html
index 9d8ec1e0ad64a28ff1544f2fc0433e7ba73350db..6affb07eac267fd9f6ab6fbb8857a4aec479ddd2 100644
--- a/client/src/app/instance/search/components/result/datatable.component.html
+++ b/client/src/app/instance/search/components/result/datatable.component.html
@@ -1,11 +1,11 @@
 <app-spinner *ngIf="dataIsLoading"></app-spinner>
 
 <div class="table-responsive" *ngIf="dataIsLoaded">
-    <table class="table table-hover" aria-describedby="List of results">
+    <table id="datatable" class="table table-hover" aria-describedby="List of results">
         <thead>
             <tr>
                 <th *ngIf="dataset.datatable_selectable_rows" scope="col" class="select">#</th>
-                <th *ngFor="let attribute of getOutputList()" scope="col">
+                <th *ngFor="let attribute of getOutputList()" scope="col" draggable="true" class="datatable-title">
                     <app-attribute-label [label]="attribute.label" [description]="attribute.description"></app-attribute-label>
                     &nbsp;
                     <span *ngIf="attribute.id === sortedCol && attribute.order_by" class="pl-2" class="clickable" (click)="sort(attribute.id)">
diff --git a/client/src/app/instance/search/components/result/datatable.component.scss b/client/src/app/instance/search/components/result/datatable.component.scss
index d40e1d1dac814cf5d102188e107a1358420f18ff..2894dff1a7b6893f06b5988777ef77ce7fa5d7a5 100644
--- a/client/src/app/instance/search/components/result/datatable.component.scss
+++ b/client/src/app/instance/search/components/result/datatable.component.scss
@@ -9,6 +9,11 @@
 
 table th:not(.select) {
     min-width: 130px;
+    cursor: move;
+}
+
+.over {
+    border-right: 3px dotted #666;
 }
 
 .data-selected {
diff --git a/client/src/app/instance/search/components/result/datatable.component.ts b/client/src/app/instance/search/components/result/datatable.component.ts
index 95cd763f07618d853931201f0268fa447ae117d9..46ca4651cbcbd9b75d7908ae71a9181c737b9880 100644
--- a/client/src/app/instance/search/components/result/datatable.component.ts
+++ b/client/src/app/instance/search/components/result/datatable.component.ts
@@ -7,7 +7,7 @@
  * file that was distributed with this source code.
  */
 
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
 
 import {
     Instance,
@@ -30,7 +30,7 @@ import { Pagination, PaginationOrder, SearchQueryParams } from 'src/app/instance
     templateUrl: 'datatable.component.html',
     styleUrls: ['datatable.component.scss'],
 })
-export class DatatableComponent implements OnInit {
+export class DatatableComponent implements OnInit, OnChanges {
     @Input() dataset: Dataset;
     @Input() instance: Instance;
     @Input() attributeList: Attribute[];
@@ -42,6 +42,7 @@ export class DatatableComponent implements OnInit {
     @Input() dataIsLoaded: boolean;
     @Input() selectedData: any[] = [];
     @Output() retrieveData: EventEmitter<Pagination> = new EventEmitter();
+    @Output() updateOutputList: EventEmitter<number[]> = new EventEmitter();
     @Output() addSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() deleteSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() downloadFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
@@ -62,6 +63,100 @@ export class DatatableComponent implements OnInit {
         }));
     }
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.dataIsLoaded && changes.dataIsLoaded.currentValue) {
+            Promise.resolve(null).then(() => {
+                // Query the table
+                const table = document.getElementById('datatable');
+
+                let dragSrcEl;
+                let outputSrcId;
+                let clonedOutputList;
+                const updateOutputList = this.updateOutputList;
+
+                const getOutputList = () => [...this.outputList];
+
+                function handleDragStart(e) {
+                    this.style.opacity = '0.4';
+                    clonedOutputList = getOutputList();
+
+                    dragSrcEl = this;
+                    for (let i = 0; i < items.length; i++) {
+                        if (items[i] === this) {
+                            outputSrcId = clonedOutputList[i];
+                        }
+                    }
+                }
+
+                function handleDragOver(e) {
+                    e.preventDefault();
+                    return false;
+                }
+
+                function handleDragEnter(e) {
+                    this.classList.add('over');
+                }
+
+                function handleDragLeave(e) {
+                    this.classList.remove('over');
+                }
+
+                function handleDragEnd(e) {
+                    this.style.opacity = '1';
+
+                    items.forEach(function (item) {
+                        item.classList.remove('over');
+                    });
+                }
+
+                function handleDrop(e) {
+                    e.stopPropagation(); // stops the browser from redirecting.
+
+                    if (dragSrcEl !== this) {
+
+                        // Remove src attribute ID into cloned output list
+                        const index = clonedOutputList.indexOf(outputSrcId);
+                        clonedOutputList.splice(index, 1);
+
+                        console.log(clonedOutputList);
+
+                        // Add src attribute ID
+                        for (let i = 0; i < items.length; i++) {
+                            if (items[i] === this) {
+                                clonedOutputList.splice(i, 0, outputSrcId);
+                            }
+                        }
+
+                        // Dispatch new output list
+                        updateOutputList.emit(clonedOutputList);
+                    }
+
+                    return false;
+                }
+
+                let items = table.querySelectorAll('.datatable-title');
+                items.forEach(function (item) {
+                    item.addEventListener('dragstart', handleDragStart);
+                    item.addEventListener('dragover', handleDragOver);
+                    item.addEventListener('dragenter', handleDragEnter);
+                    item.addEventListener('dragleave', handleDragLeave);
+                    item.addEventListener('dragend', handleDragEnd);
+                    item.addEventListener('drop', handleDrop);
+                });
+            });
+        }
+
+        if (changes.outputList && !changes.outputList.firstChange) {
+            Promise.resolve(null).then(() => this.retrieveData.emit({
+                dname: this.dataset.name,
+                page: this.page,
+                nbItems: this.nbItems,
+                sortedCol: this.sortedCol,
+                order: this.sortedOrder
+            }));
+        }
+    }
+
     /**
      * Returns renderer configuration for the given attribute.
      *
@@ -97,8 +192,7 @@ export class DatatableComponent implements OnInit {
      * @return Attribute[]
      */
     getOutputList(): Attribute[] {
-        return this.attributeList
-            .filter(a => this.outputList.includes(a.id));
+        return this.outputList.map(attributeId => this.attributeList.find(a => a.id === attributeId));
     }
 
     /**
diff --git a/client/src/app/instance/search/containers/result.component.html b/client/src/app/instance/search/containers/result.component.html
index 3bf167ec96ca6be076b8b88db8abf1b0d3290771..8e355cb02fdd2d4013733ce1d34d5da2c37842e4 100644
--- a/client/src/app/instance/search/containers/result.component.html
+++ b/client/src/app/instance/search/containers/result.component.html
@@ -104,6 +104,7 @@
                 [dataIsLoaded]="dataIsLoaded | async"
                 [selectedData]="selectedData | async"
                 (retrieveData)="retrieveData($event)"
+                (updateOutputList)="updateOutputList($event)"
                 (addSelectedData)="addSearchData($event)"
                 (deleteSelectedData)="deleteSearchData($event)"
                 (downloadFile)="downloadFile($event)">
diff --git a/client/src/app/instance/search/containers/result.component.ts b/client/src/app/instance/search/containers/result.component.ts
index b844c58262d593606dda50b1569c6144b319d695..83ab3e45bf985c4ade37ec1095ada5fb809058df 100644
--- a/client/src/app/instance/search/containers/result.component.ts
+++ b/client/src/app/instance/search/containers/result.component.ts
@@ -158,6 +158,15 @@ export class ResultComponent extends AbstractSearchComponent {
         this.store.dispatch(archiveActions.startTaskCreateArchive({ query }));
     }
 
+    /**
+     * Dispatches action to update output list selection with the given updated output list.
+     *
+     * @param  {number[]} outputList - The updated output list.
+     */
+    updateOutputList(outputList: number[]): void {
+        this.store.dispatch(searchActions.updateOutputList({ outputList }));
+    }
+
     /**
      * Dispatches action to destroy search results.
      */