diff --git a/app.js b/app.js
index 040129310f2b1f2263823ab1cfdb6beeb2c6ae30..ecc0c79a39df1bc454d682582167e2a535db6000 100644
--- a/app.js
+++ b/app.js
@@ -14,7 +14,6 @@ require.config({
     'leaflet.label': '../node_modules/leaflet-label/dist/leaflet.label',
     'chroma-js': '../node_modules/chroma-js/chroma.min',
     'moment': '../node_modules/moment/moment',
-    'tablesort': '../node_modules/tablesort/src/tablesort',
     'd3': '../node_modules/d3/d3.min',
     'virtual-dom': '../node_modules/virtual-dom/dist/virtual-dom',
     'rbush': '../node_modules/rbush/rbush',
diff --git a/lib/infobox/node.js b/lib/infobox/node.js
index 55802a783c2eb8e582bf256d963283f2f629f5c8..c10dff19f4116c109a6cbc4f31f5ba1be6dd8e3b 100644
--- a/lib/infobox/node.js
+++ b/lib/infobox/node.js
@@ -1,5 +1,5 @@
-define(['chroma-js', 'moment', 'tablesort', 'helper'],
-  function (chroma, moment, tablesort, helper) {
+define(['sorttable', 'virtual-dom', 'chroma-js', 'moment', 'helper'],
+  function (SortTable, V, chroma, moment, helper) {
     'use strict';
 
     function showGeoURI(d) {
@@ -189,6 +189,30 @@ define(['chroma-js', 'moment', 'tablesort', 'helper'],
 
     return function (config, el, router, d) {
       var linkScale = chroma.scale(chroma.bezier(['#04C714', '#FF5500', '#F02311'])).domain([1, 5]);
+      function renderNeighbourRow(n) {
+        var icons = [];
+        var name = [];
+        var unknown = !(n.node);
+
+        icons.push(V.h('span', { className: n.incoming ? 'ion-ios-arrow-thin-left' : 'ion-ios-arrow-thin-right' }));
+        if (!unknown && helper.hasLocation(n.node)) {
+          icons.push(V.h('span', { className: 'ion-location' }));
+        }
+
+        if (!unknown) {
+          name.push(V.h('a', { href: router.getUrl({ n: n.node.nodeinfo.node_id }), onclick: router.node(n.node), className: 'online' }, n.node.nodeinfo.hostname));
+        } else {
+          name.push(n.link.id);
+        }
+
+        var td1 = V.h('td', icons);
+        var td2 = V.h('td', name);
+        var td3 = V.h('td', { style: { color: linkScale(n.link.tq).hex() } }, helper.showTq(n.link));
+        var td4 = V.h('td', helper.showDistance(n.link));
+
+        return V.h('tr', [td1, td2, td3, td4]);
+      }
+
       var h2 = document.createElement('h2');
       h2.textContent = d.nodeinfo.hostname;
       el.appendChild(h2);
@@ -236,84 +260,34 @@ define(['chroma-js', 'moment', 'tablesort', 'helper'],
         h3.textContent = _.t('node.link', d.neighbours.length) + '(' + d.neighbours.length + ')';
         el.appendChild(h3);
 
-        var table = document.createElement('table');
-        var thead = document.createElement('thead');
-
-        var tr = document.createElement('tr');
-        var th1 = document.createElement('th');
-        th1.textContent = ' ';
-        tr.appendChild(th1);
-
-        var th2 = document.createElement('th');
-        th2.textContent = _.t('node.node', d.neighbours.length);
-        th2.classList.add('sort-default');
-        tr.appendChild(th2);
-
-        var th3 = document.createElement('th');
-        th3.textContent = _.t('node.tq');
-        tr.appendChild(th3);
-
-        var th4 = document.createElement('th');
-        th4.textContent = _.t('node.distance');
-        tr.appendChild(th4);
-
-        thead.appendChild(tr);
-        table.appendChild(thead);
-
-        var tbody = document.createElement('tbody');
-
-        d.neighbours.forEach(function (n) {
-          var unknown = !(n.node);
-          tr = document.createElement('tr');
-
-          var td1 = document.createElement('td');
-
-          var direction = document.createElement('span');
-          direction.classList.add(n.incoming ? 'ion-ios-arrow-thin-left' : 'ion-ios-arrow-thin-right');
-          td1.appendChild(direction);
-
-          if (!unknown && helper.hasLocation(n.node)) {
-            var span = document.createElement('span');
-            span.classList.add('ion-location');
-            td1.appendChild(span);
-          }
-
-          tr.appendChild(td1);
-
-          var td2 = document.createElement('td');
-
-          if (!unknown) {
-            var a1 = document.createElement('a');
-            a1.textContent = n.node.nodeinfo.hostname;
-            a1.classList.add('online');
-            a1.href = router.getUrl({ n: n.node.nodeinfo.node_id });
-            a1.onclick = router.node(n.node);
-            td2.appendChild(a1);
-          } else {
-            td2.textContent = n.id;
-          }
-
-          tr.appendChild(td2);
-
-          var td3 = document.createElement('td');
-          td3.textContent = helper.showTq(n.link);
-          td3.style.color = linkScale(n.link.tq).hex();
-          tr.appendChild(td3);
-
-          var td4 = document.createElement('td');
-          td4.textContent = helper.showDistance(n.link);
-          td4.setAttribute('data-sort', n.link.distance !== undefined ? -n.link.distance : 1);
-          tr.appendChild(td4);
-
-          tbody.appendChild(tr);
-        });
-
-        table.appendChild(tbody);
-        table.classList.add('node-links');
-
-        tablesort(table);
-
-        el.appendChild(table);
+        var headings = [{
+          name: ''
+        }, {
+          name: 'node.nodes',
+          sort: function (a, b) {
+            return a.node.nodeinfo.hostname.localeCompare(b.node.nodeinfo.hostname);
+          },
+          reverse: false
+        }, {
+          name: 'node.tq',
+          sort: function (a, b) {
+            return a.link.tq - b.link.tq;
+          },
+          reverse: true
+        }, {
+          name: 'node.distance',
+          sort: function (a, b) {
+            return (a.link.distance === undefined ? -1 : a.link.distance) -
+              (b.link.distance === undefined ? -1 : b.link.distance);
+          },
+          reverse: true
+        }];
+
+        var table = new SortTable(headings, 1, renderNeighbourRow);
+        table.el.classList.add('node-links');
+        table.setData(d.neighbours);
+
+        el.appendChild(table.el);
       }
     };
   });
diff --git a/package.json b/package.json
index 5dc005a84f8471dde89a3ade3564c5aade1a78f3..0afece87b1a5393c51923f026077b0976b1bf841 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,6 @@
     "promise-polyfill": "^6.0.2",
     "rbush": "1.4.3",
     "requirejs": "^2.3.2",
-    "tablesort": "5.0.0",
     "virtual-dom": "^2.1.1"
   }
 }
diff --git a/yarn.lock b/yarn.lock
index e35b8ec2dcd6dadeefc3d1c8851521e1fb4c0308..e807993dfd4b530bde9f434c64a3f4910cd0ccc2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -94,7 +94,7 @@ are-we-there-yet@~1.1.2:
     delegates "^1.0.0"
     readable-stream "^2.0.0 || ^1.1.13"
 
-argparse@^1.0.2, argparse@^1.0.7:
+argparse@^1.0.2:
   version "1.0.9"
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
   dependencies:
@@ -542,8 +542,8 @@ clean-css@1.1.7:
     commander "2.0.x"
 
 clean-css@4.0.x:
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.3.tgz#9da7b59301d940c345757f175e6dfa6872c7de8e"
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.4.tgz#629896cc364f3c3d00b9908ee60dd18e4c6c6462"
   dependencies:
     source-map "0.5.x"
 
@@ -1408,9 +1408,9 @@ glob-parent@^2.0.0:
   dependencies:
     is-glob "^2.0.0"
 
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0:
+  version "7.0.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -1429,9 +1429,9 @@ glob@~5.0.0:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-glob@~7.0.0:
-  version "7.0.6"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
+glob@~7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -2024,14 +2024,7 @@ js-tokens@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
 
-js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4:
-  version "3.7.0"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
-  dependencies:
-    argparse "^1.0.7"
-    esprima "^2.6.0"
-
-js-yaml@~3.5.2:
+js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@~3.5.2:
   version "3.5.5"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.5.5.tgz#0377c38017cabc7322b0d1fbcd25a491641f2fbe"
   dependencies:
@@ -3381,10 +3374,6 @@ table@^3.7.8:
     slice-ansi "0.0.4"
     string-width "^2.0.0"
 
-tablesort@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/tablesort/-/tablesort-5.0.0.tgz#66b4c3a4603bd8b5f67e2696f28dd784a1cbb00b"
-
 tar-pack@~3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae"