diff --git a/.eslintrc.json b/.eslintrc.json
index bffb357a..fc0b1b8c 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,3 +1,19 @@
 {
-  "extends": "next/core-web-vitals"
+  "extends": ["airbnb", "next/core-web-vitals", "prettier"],
+  "plugins": ["prettier"],
+  "rules": {
+    "import/order": [
+      "error",
+      {
+        "newlines-between": "always"
+      }
+    ]
+  },
+  "settings": {
+    "import/resolver": {
+      "node": {
+        "paths": ["src"]
+      }
+    }
+  }
 }
diff --git a/package.json b/package.json
index 355178f8..8db6e8f6 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
   "dependencies": {
     "@headlessui/react": "^1.6.6",
     "@tailwindcss/forms": "^0.5.3",
+    "classnames": "^2.3.1",
     "dockerode": "^3.3.4",
     "js-yaml": "^4.1.0",
     "json-rpc-2.0": "^1.4.1",
@@ -27,8 +28,16 @@
   "devDependencies": {
     "autoprefixer": "^10.4.8",
     "eslint": "8.22.0",
+    "eslint-config-airbnb": "^19.0.4",
     "eslint-config-next": "12.2.5",
+    "eslint-config-prettier": "^8.5.0",
+    "eslint-plugin-import": "^2.26.0",
+    "eslint-plugin-jsx-a11y": "^6.6.1",
+    "eslint-plugin-prettier": "^4.2.1",
+    "eslint-plugin-react": "^7.30.1",
+    "eslint-plugin-react-hooks": "^4.6.0",
     "postcss": "^8.4.16",
+    "prettier": "^2.7.1",
     "tailwindcss": "^3.1.8",
     "typescript": "^4.8.2"
   }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cfe2276f..c58fee0a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4,15 +4,24 @@ specifiers:
   '@headlessui/react': ^1.6.6
   '@tailwindcss/forms': ^0.5.3
   autoprefixer: ^10.4.8
+  classnames: ^2.3.1
   dockerode: ^3.3.4
   eslint: 8.22.0
+  eslint-config-airbnb: ^19.0.4
   eslint-config-next: 12.2.5
+  eslint-config-prettier: ^8.5.0
+  eslint-plugin-import: ^2.26.0
+  eslint-plugin-jsx-a11y: ^6.6.1
+  eslint-plugin-prettier: ^4.2.1
+  eslint-plugin-react: ^7.30.1
+  eslint-plugin-react-hooks: ^4.6.0
   js-yaml: ^4.1.0
   json-rpc-2.0: ^1.4.1
   memory-cache: ^0.2.0
   next: 12.2.5
   node-os-utils: ^1.3.7
   postcss: ^8.4.16
+  prettier: ^2.7.1
   raw-body: ^2.5.1
   react: 18.2.0
   react-dom: 18.2.0
@@ -25,6 +34,7 @@ specifiers:
 dependencies:
   '@headlessui/react': 1.6.6_biqbaboplfbrettd7655fr4n2y
   '@tailwindcss/forms': 0.5.3_tailwindcss@3.1.8
+  classnames: 2.3.1
   dockerode: 3.3.4
   js-yaml: 4.1.0
   json-rpc-2.0: 1.4.1
@@ -41,8 +51,16 @@ dependencies:
 devDependencies:
   autoprefixer: 10.4.8_postcss@8.4.16
   eslint: 8.22.0
+  eslint-config-airbnb: 19.0.4_ujj5bqj46ej3re666g22wkx75e
   eslint-config-next: 12.2.5_shit3uhl6a7megkzgoz6xssnfa
+  eslint-config-prettier: 8.5.0_eslint@8.22.0
+  eslint-plugin-import: 2.26.0_eslint@8.22.0
+  eslint-plugin-jsx-a11y: 6.6.1_eslint@8.22.0
+  eslint-plugin-prettier: 4.2.1_i2cojdczqdiurzgttlwdgf764e
+  eslint-plugin-react: 7.31.5_eslint@8.22.0
+  eslint-plugin-react-hooks: 4.6.0_eslint@8.22.0
   postcss: 8.4.16
+  prettier: 2.7.1
   tailwindcss: 3.1.8_postcss@8.4.16
   typescript: 4.8.2
 
@@ -604,6 +622,10 @@ packages:
     resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
     dev: false
 
+  /classnames/2.3.1:
+    resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==}
+    dev: false
+
   /color-convert/2.0.1:
     resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
     engines: {node: '>=7.0.0'}
@@ -625,6 +647,10 @@ packages:
     resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
     dev: true
 
+  /confusing-browser-globals/1.0.11:
+    resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==}
+    dev: true
+
   /core-js-pure/3.25.0:
     resolution: {integrity: sha512-IeHpLwk3uoci37yoI2Laty59+YqH9x5uR65/yiA0ARAJrTrN4YU0rmauLWfvqOuk77SlNJXj2rM6oT/dBD87+A==}
     requiresBuild: true
@@ -843,6 +869,41 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /eslint-config-airbnb-base/15.0.0_2iahngt3u2tkbdlu6s4gkur3pu:
+    resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    peerDependencies:
+      eslint: ^7.32.0 || ^8.2.0
+      eslint-plugin-import: ^2.25.2
+    dependencies:
+      confusing-browser-globals: 1.0.11
+      eslint: 8.22.0
+      eslint-plugin-import: 2.26.0_eslint@8.22.0
+      object.assign: 4.1.4
+      object.entries: 1.1.5
+      semver: 6.3.0
+    dev: true
+
+  /eslint-config-airbnb/19.0.4_ujj5bqj46ej3re666g22wkx75e:
+    resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==}
+    engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^7.32.0 || ^8.2.0
+      eslint-plugin-import: ^2.25.3
+      eslint-plugin-jsx-a11y: ^6.5.1
+      eslint-plugin-react: ^7.28.0
+      eslint-plugin-react-hooks: ^4.3.0
+    dependencies:
+      eslint: 8.22.0
+      eslint-config-airbnb-base: 15.0.0_2iahngt3u2tkbdlu6s4gkur3pu
+      eslint-plugin-import: 2.26.0_eslint@8.22.0
+      eslint-plugin-jsx-a11y: 6.6.1_eslint@8.22.0
+      eslint-plugin-react: 7.31.5_eslint@8.22.0
+      eslint-plugin-react-hooks: 4.6.0_eslint@8.22.0
+      object.assign: 4.1.4
+      object.entries: 1.1.5
+    dev: true
+
   /eslint-config-next/12.2.5_shit3uhl6a7megkzgoz6xssnfa:
     resolution: {integrity: sha512-SOowilkqPzW6DxKp3a3SYlrfPi5Ajs9MIzp9gVfUDxxH9QFM5ElkR1hX5m/iICJuvCbWgQqFBiA3mCMozluniw==}
     peerDependencies:
@@ -868,6 +929,15 @@ packages:
       - supports-color
     dev: true
 
+  /eslint-config-prettier/8.5.0_eslint@8.22.0:
+    resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+    dependencies:
+      eslint: 8.22.0
+    dev: true
+
   /eslint-import-resolver-node/0.3.6:
     resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==}
     dependencies:
@@ -925,6 +995,64 @@ packages:
       - supports-color
     dev: true
 
+  /eslint-module-utils/2.7.4_7gfxlqsjhuntdifxknjgbjwpbu:
+    resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: '*'
+      eslint-import-resolver-node: '*'
+      eslint-import-resolver-typescript: '*'
+      eslint-import-resolver-webpack: '*'
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+      eslint:
+        optional: true
+      eslint-import-resolver-node:
+        optional: true
+      eslint-import-resolver-typescript:
+        optional: true
+      eslint-import-resolver-webpack:
+        optional: true
+    dependencies:
+      debug: 3.2.7
+      eslint: 8.22.0
+      eslint-import-resolver-node: 0.3.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /eslint-plugin-import/2.26.0_eslint@8.22.0:
+    resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+    dependencies:
+      array-includes: 3.1.5
+      array.prototype.flat: 1.3.0
+      debug: 2.6.9
+      doctrine: 2.1.0
+      eslint: 8.22.0
+      eslint-import-resolver-node: 0.3.6
+      eslint-module-utils: 2.7.4_7gfxlqsjhuntdifxknjgbjwpbu
+      has: 1.0.3
+      is-core-module: 2.10.0
+      is-glob: 4.0.3
+      minimatch: 3.1.2
+      object.values: 1.1.5
+      resolve: 1.22.1
+      tsconfig-paths: 3.14.1
+    transitivePeerDependencies:
+      - eslint-import-resolver-typescript
+      - eslint-import-resolver-webpack
+      - supports-color
+    dev: true
+
   /eslint-plugin-import/2.26.0_oqagwj4pgbbpoeodu52b4a34xi:
     resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
     engines: {node: '>=4'}
@@ -978,6 +1106,23 @@ packages:
       semver: 6.3.0
     dev: true
 
+  /eslint-plugin-prettier/4.2.1_i2cojdczqdiurzgttlwdgf764e:
+    resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
+    engines: {node: '>=12.0.0'}
+    peerDependencies:
+      eslint: '>=7.28.0'
+      eslint-config-prettier: '*'
+      prettier: '>=2.0.0'
+    peerDependenciesMeta:
+      eslint-config-prettier:
+        optional: true
+    dependencies:
+      eslint: 8.22.0
+      eslint-config-prettier: 8.5.0_eslint@8.22.0
+      prettier: 2.7.1
+      prettier-linter-helpers: 1.0.0
+    dev: true
+
   /eslint-plugin-react-hooks/4.6.0_eslint@8.22.0:
     resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
     engines: {node: '>=10'}
@@ -1123,6 +1268,10 @@ packages:
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
     dev: true
 
+  /fast-diff/1.2.0:
+    resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
+    dev: true
+
   /fast-glob/3.2.11:
     resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
     engines: {node: '>=8.6.0'}
@@ -1931,6 +2080,19 @@ packages:
     engines: {node: '>= 0.8.0'}
     dev: true
 
+  /prettier-linter-helpers/1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      fast-diff: 1.2.0
+    dev: true
+
+  /prettier/2.7.1:
+    resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dev: true
+
   /prop-types/15.8.1:
     resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
     dependencies:
diff --git a/src/components/bookmarks/item.jsx b/src/components/bookmarks/item.jsx
index 380699bb..560bcb1a 100644
--- a/src/components/bookmarks/item.jsx
+++ b/src/components/bookmarks/item.jsx
@@ -2,20 +2,22 @@ export default function Item({ bookmark }) {
   const { hostname } = new URL(bookmark.href);
 
   return (
-    <li
-      onClick={() => {
-        window.open(bookmark.href, "_blank").focus();
-      }}
-      key={bookmark.name}
-      className="mb-3 cursor-pointer flex rounded-md font-medium text-theme-700 hover:text-theme-800 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/50 bg-white/50 hover:bg-theme-300/10 dark:bg-white/5 dark:hover:bg-white/10"
-    >
-      <div className="flex-shrink-0 flex items-center justify-center w-11 bg-theme-500/10 dark:bg-theme-900/50 text-theme-700 dark:text-theme-200 text-sm font-medium rounded-l-md">
-        {bookmark.abbr}
-      </div>
-      <div className="flex-1 flex items-center justify-between rounded-r-md ">
-        <div className="flex-1 grow pl-3 py-2 text-xs">{bookmark.name}</div>
-        <div className="px-2 py-2 truncate text-theme-500 dark:text-theme-400 opacity-50 text-xs">{hostname}</div>
-      </div>
+    <li key={bookmark.name}>
+      <button
+        type="button"
+        onClick={() => window.open(bookmark.href, "_blank").focus()}
+        className="w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-800 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/50 bg-white/50 hover:bg-theme-300/10 dark:bg-white/5 dark:hover:bg-white/10"
+      >
+        <div className="flex">
+          <div className="flex-shrink-0 flex items-center justify-center w-11 bg-theme-500/10 dark:bg-theme-900/50 text-theme-700 dark:text-theme-200 text-sm font-medium rounded-l-md">
+            {bookmark.abbr}
+          </div>
+          <div className="flex-1 flex items-center justify-between rounded-r-md ">
+            <div className="flex-1 grow pl-3 py-2 text-xs">{bookmark.name}</div>
+            <div className="px-2 py-2 truncate text-theme-500 dark:text-theme-400 opacity-50 text-xs">{hostname}</div>
+          </div>
+        </div>
+      </button>
     </li>
   );
 }
diff --git a/src/components/bookmarks/list.jsx b/src/components/bookmarks/list.jsx
index 2c080e02..3b3774c9 100644
--- a/src/components/bookmarks/list.jsx
+++ b/src/components/bookmarks/list.jsx
@@ -2,7 +2,7 @@ import Item from "components/bookmarks/item";
 
 export default function List({ bookmarks }) {
   return (
-    <ul role="list" className="mt-3 flex flex-col">
+    <ul className="mt-3 flex flex-col">
       {bookmarks.map((bookmark) => (
         <Item key={bookmark.name} bookmark={bookmark} />
       ))}
diff --git a/src/components/color-toggle.jsx b/src/components/color-toggle.jsx
index 1f9fe84c..3b98c995 100644
--- a/src/components/color-toggle.jsx
+++ b/src/components/color-toggle.jsx
@@ -1,7 +1,7 @@
-import { useContext } from "react";
+import { useContext, Fragment } from "react";
 import { IoColorPalette } from "react-icons/io5";
 import { Popover, Transition } from "@headlessui/react";
-import { Fragment } from "react";
+import classNames from "classnames";
 
 import { ColorContext } from "utils/color-context";
 
@@ -39,42 +39,38 @@ export default function ColorToggle() {
   return (
     <div className="w-full self-center">
       <Popover className="relative flex items-center">
-        {({ open }) => (
-          <>
-            <Popover.Button className="outline-none">
-              <IoColorPalette
-                className="h-5 w-5 text-theme-800 dark:text-theme-200 transition duration-150 ease-in-out"
-                aria-hidden="true"
-              />
-            </Popover.Button>
-            <Transition
-              as={Fragment}
-              enter="transition ease-out duration-200"
-              enterFrom="opacity-0 translate-y-1"
-              enterTo="opacity-100 translate-y-0"
-              leave="transition ease-in duration-150"
-              leaveFrom="opacity-100 translate-y-0"
-              leaveTo="opacity-0 translate-y-1"
-            >
-              <Popover.Panel className="absolute -top-[75px] left-0">
-                <div className="rounded-md shadow-lg ring-1 ring-black ring-opacity-5">
-                  <div className="relative grid gap-2 p-2 grid-cols-11 shadow-theme-900/10 dark:shadow-theme-900 rounded-md shadow-md">
-                    {colors.map((color) => (
-                      <button role="button" onClick={() => setColor(color)} key={color}>
-                        <div
-                          className={
-                            (active == color ? "border-2" : "border-0") +
-                            ` rounded-md w-5 h-5 border-black/50 dark:border-white/50 theme-${color} bg-theme-500`
-                          }
-                        />
-                      </button>
-                    ))}
-                  </div>
-                </div>
-              </Popover.Panel>
-            </Transition>
-          </>
-        )}
+        <Popover.Button className="outline-none">
+          <IoColorPalette
+            className="h-5 w-5 text-theme-800 dark:text-theme-200 transition duration-150 ease-in-out"
+            aria-hidden="true"
+          />
+        </Popover.Button>
+        <Transition
+          as={Fragment}
+          enter="transition ease-out duration-200"
+          enterFrom="opacity-0 translate-y-1"
+          enterTo="opacity-100 translate-y-0"
+          leave="transition ease-in duration-150"
+          leaveFrom="opacity-100 translate-y-0"
+          leaveTo="opacity-0 translate-y-1"
+        >
+          <Popover.Panel className="absolute -top-[75px] left-0">
+            <div className="rounded-md shadow-lg ring-1 ring-black ring-opacity-5">
+              <div className="relative grid gap-2 p-2 grid-cols-11 shadow-theme-900/10 dark:shadow-theme-900 rounded-md shadow-md">
+                {colors.map((color) => (
+                  <button type="button" onClick={() => setColor(color)} key={color}>
+                    <div
+                      className={classNames(
+                        active === color ? "border-2" : "border-0",
+                        `rounded-md w-5 h-5 border-black/50 dark:border-white/50 theme-${color} bg-theme-500`
+                      )}
+                    />
+                  </button>
+                ))}
+              </div>
+            </div>
+          </Popover.Panel>
+        </Transition>
       </Popover>
     </div>
   );
diff --git a/src/components/greeting.jsx b/src/components/greeting.jsx
index 5412a0fc..c660ecd9 100644
--- a/src/components/greeting.jsx
+++ b/src/components/greeting.jsx
@@ -1,5 +1,4 @@
 export default function Greeting() {
-  const name = process.env.NEXT_PUBLIC_DISPLAY_NAME;
   const hour = new Date().getHours();
 
   let day = "day";
@@ -12,9 +11,5 @@ export default function Greeting() {
     day = "evening";
   }
 
-  return (
-    <div className="self-end grow text-2xl font-thin text-theme-800 dark:text-theme-200">
-      Good {day}
-    </div>
-  );
+  return <div className="self-end grow text-2xl font-thin text-theme-800 dark:text-theme-200">Good {day}</div>;
 }
diff --git a/src/components/modal.jsx b/src/components/modal.jsx
deleted file mode 100644
index f683b5e9..00000000
--- a/src/components/modal.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { Fragment, useRef, useState, Children } from "react";
-import { Dialog, Transition } from "@headlessui/react";
-
-function classNames(...classes) {
-  return classes.filter(Boolean).join(" ");
-}
-
-const Modal = ({ Toggle, Content }) => {
-  const [open, setOpen] = useState(false);
-  const cancelButtonRef = useRef(null);
-
-  return (
-    <>
-      <Toggle open={open} setOpen={setOpen} />
-      <Transition.Root show={open} as={Fragment}>
-        <Dialog
-          as="div"
-          className="relative z-10"
-          initialFocus={cancelButtonRef}
-          onClose={setOpen}
-        >
-          <Transition.Child
-            as={Fragment}
-            enter="ease-out duration-300"
-            enterFrom="opacity-0"
-            enterTo="opacity-100"
-            leave="ease-in duration-200"
-            leaveFrom="opacity-100"
-            leaveTo="opacity-0"
-          >
-            <div className="fixed inset-0 bg-theme-900/90 transition-opacity" />
-          </Transition.Child>
-
-          <div className="fixed z-10 inset-0 overflow-y-auto">
-            <div className="flex items-center justify-center min-h-full">
-              <Transition.Child
-                as={Fragment}
-                enter="ease-out duration-300"
-                enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
-                enterTo="opacity-100 translate-y-0 sm:scale-100"
-                leave="ease-in duration-200"
-                leaveFrom="opacity-100 translate-y-0 sm:scale-100"
-                leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
-              >
-                <Dialog.Panel className="relative rounded-lg shadow-xl transform transition-all my-8 max-w-lg w-full">
-                  <Content open={open} setOpen={setOpen} />
-                </Dialog.Panel>
-              </Transition.Child>
-            </div>
-          </div>
-        </Dialog>
-      </Transition.Root>
-    </>
-  );
-};
-
-const ModalToggle = ({ open, setOpen, children }) => (
-  <div onClick={() => setOpen(!open)}>{children}</div>
-);
-
-const ModalContent = ({ open, setOpen, children }) => (
-  <div className="body">{children}</div>
-);
-
-Modal.Toggle = ModalToggle;
-Modal.Content = ModalContent;
-
-export default Modal;
diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx
index fffc6806..4cb5a055 100644
--- a/src/components/services/item.jsx
+++ b/src/components/services/item.jsx
@@ -8,28 +8,32 @@ import Docker from "./widgets/service/docker";
 function resolveIcon(icon) {
   if (icon.startsWith("http")) {
     return `/api/proxy?url=${encodeURIComponent(icon)}`;
-  } else if (icon.startsWith("/")) {
-    return icon;
-  } else {
-    if (icon.endsWith(".png")) {
-      return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}`;
-    } else {
-      return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}.png`;
-    }
   }
+
+  if (icon.startsWith("/")) {
+    return icon;
+  }
+
+  if (icon.endsWith(".png")) {
+    return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}`;
+  }
+
+  return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}.png`;
 }
 
 export default function Item({ service }) {
   return (
     <li key={service.name}>
       <Disclosure>
-        <div className={
-          (service.href && service.href !== "#" ? 'cursor-pointer ' : 'cursor-default ') +
-          'transition-all h-15 overflow-hidden mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-800 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/40 bg-white/50 hover:bg-theme-300/10 dark:bg-white/5 dark:hover:bg-white/10'
-          }>
+        <div
+          className={`${
+            service.href && service.href !== "#" ? "cursor-pointer " : "cursor-default "
+          }transition-all h-15 overflow-hidden mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-800 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/40 bg-white/50 hover:bg-theme-300/10 dark:bg-white/5 dark:hover:bg-white/10`}
+        >
           <div className="flex">
             {service.icon && (
-              <div
+              <button
+                type="button"
                 onClick={() => {
                   if (service.href && service.href !== "#") {
                     window.open(service.href, "_blank").focus();
@@ -38,10 +42,11 @@ export default function Item({ service }) {
                 className="flex-shrink-0 flex items-center justify-center w-12 "
               >
                 <Image src={resolveIcon(service.icon)} width={32} height={32} alt="logo" />
-              </div>
+              </button>
             )}
 
-            <div
+            <button
+              type="button"
               onClick={() => {
                 if (service.href && service.href !== "#") {
                   window.open(service.href, "_blank").focus();
@@ -53,9 +58,12 @@ export default function Item({ service }) {
                 {service.name}
                 <p className="text-theme-500 dark:text-theme-400 text-xs font-extralight">{service.description}</p>
               </div>
-            </div>
+            </button>
             {service.container && (
-              <Disclosure.Button as="div" className="flex-shrink-0 flex items-center justify-center w-12 cursor-pointer">
+              <Disclosure.Button
+                as="div"
+                className="flex-shrink-0 flex items-center justify-center w-12 cursor-pointer"
+              >
                 <Status service={service} />
               </Disclosure.Button>
             )}
diff --git a/src/components/services/list.jsx b/src/components/services/list.jsx
index ee519137..b6c60cc8 100644
--- a/src/components/services/list.jsx
+++ b/src/components/services/list.jsx
@@ -2,7 +2,7 @@ import Item from "components/services/item";
 
 export default function List({ services }) {
   return (
-    <ul role="list" className="mt-3 flex flex-col">
+    <ul className="mt-3 flex flex-col">
       {services.map((service) => (
         <Item key={service.name} service={service} />
       ))}
diff --git a/src/components/services/status.jsx b/src/components/services/status.jsx
index b2c0ea4c..dc903408 100644
--- a/src/components/services/status.jsx
+++ b/src/components/services/status.jsx
@@ -1,28 +1,18 @@
 import useSWR from "swr";
 
 export default function Status({ service }) {
-  const { data, error } = useSWR(
-    `/api/docker/status/${service.container}/${service.server || ""}`
-  );
+  const { data, error } = useSWR(`/api/docker/status/${service.container}/${service.server || ""}`);
 
   if (error) {
-    return (
-      <div className="w-3 h-3 bg-rose-300 dark:bg-rose-500 rounded-full" />
-    );
+    return <div className="w-3 h-3 bg-rose-300 dark:bg-rose-500 rounded-full" />;
   }
 
   if (data && data.status === "running") {
-    return (
-      <div className="w-3 h-3 bg-emerald-300 dark:bg-emerald-500 rounded-full" />
-    );
+    return <div className="w-3 h-3 bg-emerald-300 dark:bg-emerald-500 rounded-full" />;
   }
 
   if (data && data.status === "not found") {
-    return (
-      <>
-        <div className="h-2.5 w-2.5 bg-orange-400/50 dark:bg-yellow-200/40 -rotate-45"></div>
-      </>
-    );
+    return <div className="h-2.5 w-2.5 bg-orange-400/50 dark:bg-yellow-200/40 -rotate-45" />;
   }
 
   return <div className="w-3 h-3 bg-black/20 dark:bg-white/40 rounded-full" />;
diff --git a/src/components/services/widgets/service/emby.jsx b/src/components/services/widgets/service/emby.jsx
index ac964566..c9e6e419 100644
--- a/src/components/services/widgets/service/emby.jsx
+++ b/src/components/services/widgets/service/emby.jsx
@@ -24,9 +24,9 @@ export default function Emby({ service, title = "Emby" }) {
     );
   }
 
-  const playing = sessionsData.filter((session) => session.hasOwnProperty("NowPlayingItem"));
+  const playing = sessionsData.filter((session) => session?.NowPlayingItem);
   const transcoding = sessionsData.filter(
-    (session) => session.hasOwnProperty("PlayState") && session.PlayState.PlayMethod === "Transcode"
+    (session) => session?.PlayState && session.PlayState.PlayMethod === "Transcode"
   );
   const bitrate = playing.reduce((acc, session) => acc + session.NowPlayingItem.Bitrate, 0);
 
diff --git a/src/components/services/widgets/service/rutorrent.jsx b/src/components/services/widgets/service/rutorrent.jsx
index 5dd11333..ff9fb2a3 100644
--- a/src/components/services/widgets/service/rutorrent.jsx
+++ b/src/components/services/widgets/service/rutorrent.jsx
@@ -25,13 +25,9 @@ export default function Rutorrent({ service }) {
     );
   }
 
-  const upload = statusData.reduce((acc, torrent) => {
-    return acc + parseInt(torrent["d.get_up_rate"]);
-  }, 0);
+  const upload = statusData.reduce((acc, torrent) => acc + parseInt(torrent["d.get_up_rate"], 10), 0);
 
-  const download = statusData.reduce((acc, torrent) => {
-    return acc + parseInt(torrent["d.get_down_rate"]);
-  }, 0);
+  const download = statusData.reduce((acc, torrent) => acc + parseInt(torrent["d.get_down_rate"], 10), 0);
 
   const active = statusData.filter((torrent) => torrent["d.get_state"] === "1");
 
diff --git a/src/components/services/widgets/service/tautulli.jsx b/src/components/services/widgets/service/tautulli.jsx
index 32fe91b3..a3dce1c1 100644
--- a/src/components/services/widgets/service/tautulli.jsx
+++ b/src/components/services/widgets/service/tautulli.jsx
@@ -24,7 +24,7 @@ export default function Tautulli({ service }) {
     );
   }
 
-  const data = statsData.response.data;
+  const { data } = statsData.response;
 
   return (
     <Widget>
diff --git a/src/components/widgets/openweathermap/icon.jsx b/src/components/widgets/openweathermap/icon.jsx
index 975c1f7e..da65bc91 100644
--- a/src/components/widgets/openweathermap/icon.jsx
+++ b/src/components/widgets/openweathermap/icon.jsx
@@ -1,7 +1,7 @@
 import mapIcon from "utils/owm-condition-map";
 
 export default function Icon({ condition, timeOfDay }) {
-  const Icon = mapIcon(condition, timeOfDay);
+  const IconComponent = mapIcon(condition, timeOfDay);
 
-  return <Icon className="w-10 h-10 text-theme-800 dark:text-theme-200"></Icon>;
+  return <IconComponent className="w-10 h-10 text-theme-800 dark:text-theme-200" />;
 }
diff --git a/src/components/widgets/openweathermap/weather.jsx b/src/components/widgets/openweathermap/weather.jsx
index a2074353..c1ab9f05 100644
--- a/src/components/widgets/openweathermap/weather.jsx
+++ b/src/components/widgets/openweathermap/weather.jsx
@@ -6,7 +6,7 @@ import Icon from "./icon";
 export default function OpenWeatherMap({ options }) {
   const { data, error } = useSWR(`/api/widgets/openweathermap?${new URLSearchParams(options).toString()}`);
 
-  if (error || data?.cod == 401) {
+  if (error || data?.cod === 401) {
     return (
       <div className="flex flex-col">
         <div className="flex flex-row items-center justify-end">
@@ -23,11 +23,11 @@ export default function OpenWeatherMap({ options }) {
   }
 
   if (!data) {
-    return <div className="flex flex-row items-center"></div>;
+    return <div className="flex flex-row items-center" />;
   }
 
   if (data.error) {
-    return <div className="flex flex-row items-center"></div>;
+    return <div className="flex flex-row items-center" />;
   }
 
   return (
diff --git a/src/components/widgets/resources/disk.jsx b/src/components/widgets/resources/disk.jsx
index e181c604..f78a3a2e 100644
--- a/src/components/widgets/resources/disk.jsx
+++ b/src/components/widgets/resources/disk.jsx
@@ -1,6 +1,7 @@
 import useSWR from "swr";
 import { FiHardDrive } from "react-icons/fi";
 import { BiError } from "react-icons/bi";
+
 import { formatBytes } from "utils/stats-helpers";
 
 export default function Disk({ options }) {
diff --git a/src/components/widgets/resources/memory.jsx b/src/components/widgets/resources/memory.jsx
index 9f2b341c..e8d60ccc 100644
--- a/src/components/widgets/resources/memory.jsx
+++ b/src/components/widgets/resources/memory.jsx
@@ -1,6 +1,7 @@
 import useSWR from "swr";
 import { FaMemory } from "react-icons/fa";
 import { BiError } from "react-icons/bi";
+
 import { formatBytes } from "utils/stats-helpers";
 
 export default function Memory() {
diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx
index d5b7705f..711e97fb 100644
--- a/src/components/widgets/resources/resources.jsx
+++ b/src/components/widgets/resources/resources.jsx
@@ -4,19 +4,17 @@ import Memory from "./memory";
 
 export default function Resources({ options }) {
   return (
-    <>
-      <div className="flex flex-col max-w:full basis-1/2 sm:basis-auto self-center">
-        <div className="flex flex-row space-x-4 self-center">
-          {options.cpu && <Cpu />}
-          {options.memory && <Memory />}
-          {options.disk && <Disk options={options} />}
-        </div>
-        {options.label && (
-          <div className="border-t-2 border-theme-800 dark:border-theme-200 mt-1 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">
-            {options.label}
-          </div>
-        )}
+    <div className="flex flex-col max-w:full basis-1/2 sm:basis-auto self-center">
+      <div className="flex flex-row space-x-4 self-center">
+        {options.cpu && <Cpu />}
+        {options.memory && <Memory />}
+        {options.disk && <Disk options={options} />}
       </div>
-    </>
+      {options.label && (
+        <div className="border-t-2 border-theme-800 dark:border-theme-200 mt-1 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">
+          {options.label}
+        </div>
+      )}
+    </div>
   );
 }
diff --git a/src/components/widgets/search/search.jsx b/src/components/widgets/search/search.jsx
index 48ba3b2e..d1805d72 100644
--- a/src/components/widgets/search/search.jsx
+++ b/src/components/widgets/search/search.jsx
@@ -30,7 +30,7 @@ export default function Search({ options }) {
   const [query, setQuery] = useState("");
 
   if (!provider) {
-    return <></>;
+    return null;
   }
 
   function handleSubmit(event) {
@@ -49,11 +49,10 @@ export default function Search({ options }) {
 
   return (
     <form className="flex-col relative h-8 my-4 min-w-full md:min-w-fit grow" onSubmit={handleSubmit}>
-      <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-theme-200"></div>
+      <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-theme-200" />
       <input
         type="search"
-        autoFocus
-        className={`overflow-hidden w-full placeholder-theme-900 text-xs text-theme-900 bg-theme-50 rounded-md border border-theme-300 focus:ring-theme-500 focus:border-theme-500 dark:bg-theme-800 dark:border-theme-600 dark:placeholder-theme-400 dark:text-white dark:focus:ring-theme-500 dark:focus:border-theme-500 h-full`}
+        className="overflow-hidden w-full placeholder-theme-900 text-xs text-theme-900 bg-theme-50 rounded-md border border-theme-300 focus:ring-theme-500 focus:border-theme-500 dark:bg-theme-800 dark:border-theme-600 dark:placeholder-theme-400 dark:text-white dark:focus:ring-theme-500 dark:focus:border-theme-500 h-full"
         placeholder="Search..."
         onChange={(s) => setQuery(s.currentTarget.value)}
         required
diff --git a/src/components/widgets/weather/icon.jsx b/src/components/widgets/weather/icon.jsx
index b6105254..e501b0a8 100644
--- a/src/components/widgets/weather/icon.jsx
+++ b/src/components/widgets/weather/icon.jsx
@@ -1,7 +1,7 @@
 import mapIcon from "utils/condition-map";
 
 export default function Icon({ condition, timeOfDay }) {
-  const Icon = mapIcon(condition, timeOfDay);
+  const IconComponent = mapIcon(condition, timeOfDay);
 
-  return <Icon className="w-10 h-10 text-theme-800 dark:text-theme-200"></Icon>;
+  return <IconComponent className="w-10 h-10 text-theme-800 dark:text-theme-200" />;
 }
diff --git a/src/components/widgets/weather/weather.jsx b/src/components/widgets/weather/weather.jsx
index 87d03786..36ca9cbd 100644
--- a/src/components/widgets/weather/weather.jsx
+++ b/src/components/widgets/weather/weather.jsx
@@ -4,7 +4,6 @@ import { BiError } from "react-icons/bi";
 import Icon from "./icon";
 
 export default function WeatherApi({ options }) {
-  console.log(options);
   const { data, error } = useSWR(`/api/widgets/weather?${new URLSearchParams(options).toString()}`);
 
   if (error) {
@@ -24,11 +23,11 @@ export default function WeatherApi({ options }) {
   }
 
   if (!data) {
-    return <div className="flex flex-row items-center justify-end"></div>;
+    return <div className="flex flex-row items-center justify-end" />;
   }
 
   if (data.error) {
-    return <div className="flex flex-row items-center justify-end"></div>;
+    return <div className="flex flex-row items-center justify-end" />;
   }
 
   return (
diff --git a/src/pages/_app.js b/src/pages/_app.jsx
similarity index 88%
rename from src/pages/_app.js
rename to src/pages/_app.jsx
index 58e3293b..7c12e38f 100644
--- a/src/pages/_app.js
+++ b/src/pages/_app.jsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/jsx-props-no-spreading */
 import { SWRConfig } from "swr";
 import "styles/globals.css";
 import "styles/weather-icons.css";
diff --git a/src/pages/_document.js b/src/pages/_document.jsx
similarity index 100%
rename from src/pages/_document.js
rename to src/pages/_document.jsx
diff --git a/src/pages/api/bookmarks.js b/src/pages/api/bookmarks.js
index ee77065d..c50add44 100644
--- a/src/pages/api/bookmarks.js
+++ b/src/pages/api/bookmarks.js
@@ -1,6 +1,8 @@
 import { promises as fs } from "fs";
 import path from "path";
+
 import yaml from "js-yaml";
+
 import checkAndCopyConfig from "utils/config";
 
 export default async function handler(req, res) {
@@ -11,17 +13,13 @@ export default async function handler(req, res) {
   const bookmarks = yaml.load(fileContents);
 
   // map easy to write YAML objects into easy to consume JS arrays
-  const bookmarksArray = bookmarks.map((group) => {
-    return {
-      name: Object.keys(group)[0],
-      bookmarks: group[Object.keys(group)[0]].map((entries) => {
-        return {
-          name: Object.keys(entries)[0],
-          ...entries[Object.keys(entries)[0]][0],
-        };
-      }),
-    };
-  });
+  const bookmarksArray = bookmarks.map((group) => ({
+    name: Object.keys(group)[0],
+    bookmarks: group[Object.keys(group)[0]].map((entries) => ({
+      name: Object.keys(entries)[0],
+      ...entries[Object.keys(entries)[0]][0],
+    })),
+  }));
 
   res.send(bookmarksArray);
 }
diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js
index 34f387b7..382a8924 100644
--- a/src/pages/api/docker/stats/[...service].js
+++ b/src/pages/api/docker/stats/[...service].js
@@ -1,4 +1,5 @@
 import Docker from "dockerode";
+
 import getDockerArguments from "utils/docker";
 
 export default async function handler(req, res) {
@@ -21,30 +22,30 @@ export default async function handler(req, res) {
     // bad docker connections can result in a <Buffer ...> object?
     // in any case, this ensures the result is the expected array
     if (!Array.isArray(containers)) {
-      return res.status(500).send({
+      res.status(500).send({
         error: "query failed",
       });
+      return;
     }
 
-    const containerNames = containers.map((container) => {
-      return container.Names[0].replace(/^\//, "");
-    });
+    const containerNames = containers.map((container) => container.Names[0].replace(/^\//, ""));
     const containerExists = containerNames.includes(containerName);
 
     if (!containerExists) {
-      return res.status(200).send({
+      res.status(200).send({
         error: "not found",
       });
+      return;
     }
 
     const container = docker.getContainer(containerName);
     const stats = await container.stats({ stream: false });
 
-    return res.status(200).json({
-      stats: stats,
+    res.status(200).json({
+      stats,
     });
   } catch {
-    return res.status(500).send({
+    res.status(500).send({
       error: "unknown error",
     });
   }
diff --git a/src/pages/api/docker/status/[...service].js b/src/pages/api/docker/status/[...service].js
index d59fffd7..2aafe687 100644
--- a/src/pages/api/docker/status/[...service].js
+++ b/src/pages/api/docker/status/[...service].js
@@ -1,4 +1,5 @@
 import Docker from "dockerode";
+
 import getDockerArguments from "utils/docker";
 
 export default async function handler(req, res) {
@@ -25,9 +26,7 @@ export default async function handler(req, res) {
       });
     }
 
-    const containerNames = containers.map((container) => {
-      return container.Names[0].replace(/^\//, "");
-    });
+    const containerNames = containers.map((container) => container.Names[0].replace(/^\//, ""));
     const containerExists = containerNames.includes(containerName);
 
     if (!containerExists) {
diff --git a/src/pages/api/proxy.js b/src/pages/api/proxy.js
index 1a8b1811..ff7ca357 100644
--- a/src/pages/api/proxy.js
+++ b/src/pages/api/proxy.js
@@ -1,4 +1,5 @@
 import https from "https";
+
 import getRawBody from "raw-body";
 
 import { httpRequest, httpsRequest } from "utils/http";
@@ -11,7 +12,8 @@ export const config = {
 
 export default async function handler(req, res) {
   const headers = ["X-API-Key", "Authorization"].reduce((obj, key) => {
-    if (req.headers && req.headers.hasOwnProperty(key.toLowerCase())) {
+    if (req.headers && Object.prototype.hasOwnProperty.call(req.headers, key.toLowerCase())) {
+      // eslint-disable-next-line no-param-reassign
       obj[key] = req.headers[key.toLowerCase()];
     }
     return obj;
@@ -29,23 +31,9 @@ export default async function handler(req, res) {
     const [status, contentType, data] = await httpsRequest(url, {
       agent: httpsAgent,
       method: req.method,
-      headers: headers,
+      headers,
       body:
-        req.method == "GET" || req.method == "HEAD"
-          ? null
-          : await getRawBody(req, {
-              encoding: "utf8",
-            }),
-    });
-
-    res.setHeader("Content-Type", contentType);
-    return res.status(status).send(data);
-  } else {
-    const [status, contentType, data] = await httpRequest(url, {
-      method: req.method,
-      headers: headers,
-      body:
-        req.method == "GET" || req.method == "HEAD"
+        req.method === "GET" || req.method === "HEAD"
           ? null
           : await getRawBody(req, {
               encoding: "utf8",
@@ -55,4 +43,17 @@ export default async function handler(req, res) {
     res.setHeader("Content-Type", contentType);
     return res.status(status).send(data);
   }
+  const [status, contentType, data] = await httpRequest(url, {
+    method: req.method,
+    headers,
+    body:
+      req.method === "GET" || req.method === "HEAD"
+        ? null
+        : await getRawBody(req, {
+            encoding: "utf8",
+          }),
+  });
+
+  res.setHeader("Content-Type", contentType);
+  return res.status(status).send(data);
 }
diff --git a/src/pages/api/services/index.js b/src/pages/api/services/index.js
index 572ab404..40f1ea69 100644
--- a/src/pages/api/services/index.js
+++ b/src/pages/api/services/index.js
@@ -1,6 +1,8 @@
 import { promises as fs } from "fs";
 import path from "path";
+
 import yaml from "js-yaml";
+
 import checkAndCopyConfig from "utils/config";
 
 export default async function handler(req, res) {
@@ -11,30 +13,28 @@ export default async function handler(req, res) {
   const services = yaml.load(fileContents);
 
   // map easy to write YAML objects into easy to consume JS arrays
-  const servicesArray = services.map((group) => {
-    return {
-      name: Object.keys(group)[0],
-      services: group[Object.keys(group)[0]].map((entries) => {
-        const { widget, ...service } = entries[Object.keys(entries)[0]];
-        let res = {
-          name: Object.keys(entries)[0],
-          ...service,
+  const servicesArray = services.map((group) => ({
+    name: Object.keys(group)[0],
+    services: group[Object.keys(group)[0]].map((entries) => {
+      const { widget, ...service } = entries[Object.keys(entries)[0]];
+      const result = {
+        name: Object.keys(entries)[0],
+        ...service,
+      };
+
+      if (widget) {
+        const { type } = widget;
+
+        result.widget = {
+          type,
+          service_group: Object.keys(group)[0],
+          service_name: Object.keys(entries)[0],
         };
+      }
 
-        if (widget) {
-          const { type } = widget;
-
-          res.widget = {
-            type: type,
-            service_group: Object.keys(group)[0],
-            service_name: Object.keys(entries)[0],
-          };
-        }
-
-        return res;
-      }),
-    };
-  });
+      return result;
+    }),
+  }));
 
   res.send(servicesArray);
 }
diff --git a/src/pages/api/services/proxy.js b/src/pages/api/services/proxy.js
index 63e047bc..970008e1 100644
--- a/src/pages/api/services/proxy.js
+++ b/src/pages/api/services/proxy.js
@@ -33,5 +33,5 @@ export default async function handler(req, res) {
     return serviceProxyHandler(req, res);
   }
 
-  res.status(403).json({ error: "Unkown proxy service type" });
+  return res.status(403).json({ error: "Unkown proxy service type" });
 }
diff --git a/src/pages/api/widgets/index.js b/src/pages/api/widgets/index.js
index 98496ad6..3e96c26b 100644
--- a/src/pages/api/widgets/index.js
+++ b/src/pages/api/widgets/index.js
@@ -1,6 +1,8 @@
 import { promises as fs } from "fs";
 import path from "path";
+
 import yaml from "js-yaml";
+
 import checkAndCopyConfig from "utils/config";
 
 export default async function handler(req, res) {
@@ -11,12 +13,10 @@ export default async function handler(req, res) {
   const widgets = yaml.load(fileContents);
 
   // map easy to write YAML objects into easy to consume JS arrays
-  const widgetsArray = widgets.map((group) => {
-    return {
-      type: Object.keys(group)[0],
-      options: { ...group[Object.keys(group)[0]] },
-    };
-  });
+  const widgetsArray = widgets.map((group) => ({
+    type: Object.keys(group)[0],
+    options: { ...group[Object.keys(group)[0]] },
+  }));
 
   res.send(widgetsArray);
 }
diff --git a/src/pages/api/widgets/openweathermap.js b/src/pages/api/widgets/openweathermap.js
index 5a687725..1bda5ebf 100644
--- a/src/pages/api/widgets/openweathermap.js
+++ b/src/pages/api/widgets/openweathermap.js
@@ -22,7 +22,7 @@ export default async function handler(req, res) {
     return res.status(400).json({ error: "Missing API key" });
   }
 
-  const api_url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}`;
+  const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}`;
 
-  res.send(await cachedFetch(api_url, cache));
+  return res.send(await cachedFetch(apiUrl, cache));
 }
diff --git a/src/pages/api/widgets/resources.js b/src/pages/api/widgets/resources.js
index f9e51b19..ceb3ff53 100644
--- a/src/pages/api/widgets/resources.js
+++ b/src/pages/api/widgets/resources.js
@@ -1,26 +1,30 @@
-import { cpu, drive, mem, netstat } from "node-os-utils";
+import { cpu, drive, mem } from "node-os-utils";
 
 export default async function handler(req, res) {
   const { type, target } = req.query;
 
-  if (type == "cpu") {
+  if (type === "cpu") {
     return res.status(200).json({
       cpu: {
         usage: await cpu.usage(1000),
         load: cpu.loadavgTime(5),
       },
     });
-  } else if (type == "disk") {
+  }
+
+  if (type === "disk") {
     return res.status(200).json({
       drive: await drive.info(target || "/"),
     });
-  } else if (type == "memory") {
+  }
+
+  if (type === "memory") {
     return res.status(200).json({
       memory: await mem.info(),
     });
-  } else {
-    return res.status(400).json({
-      error: "invalid type",
-    });
   }
+
+  return res.status(400).json({
+    error: "invalid type",
+  });
 }
diff --git a/src/pages/api/widgets/weather.js b/src/pages/api/widgets/weather.js
index a7da1093..2cda1c04 100644
--- a/src/pages/api/widgets/weather.js
+++ b/src/pages/api/widgets/weather.js
@@ -22,7 +22,7 @@ export default async function handler(req, res) {
     return res.status(400).json({ error: "Missing API key" });
   }
 
-  const api_url = `http://api.weatherapi.com/v1/current.json?q=${latitude},${longitude}&key=${apiKey}`;
+  const apiUrl = `http://api.weatherapi.com/v1/current.json?q=${latitude},${longitude}&key=${apiKey}`;
 
-  res.send(await cachedFetch(api_url, cache));
+  return res.send(await cachedFetch(apiUrl, cache));
 }
diff --git a/src/pages/index.js b/src/pages/index.jsx
similarity index 81%
rename from src/pages/index.js
rename to src/pages/index.jsx
index 009ee00e..c0121a55 100644
--- a/src/pages/index.js
+++ b/src/pages/index.jsx
@@ -2,13 +2,11 @@ import useSWR from "swr";
 import Head from "next/head";
 import dynamic from "next/dynamic";
 
-import { ThemeProvider } from "utils/theme-context";
-
 import ServicesGroup from "components/services/group";
 import BookmarksGroup from "components/bookmarks/group";
 import Widget from "components/widget";
 import { ColorProvider } from "utils/color-context";
-import Search from "components/widgets/search/search";
+import { ThemeProvider } from "utils/theme-context";
 
 const ThemeToggle = dynamic(() => import("components/theme-toggle"), {
   ssr: false,
@@ -19,12 +17,11 @@ const ColorToggle = dynamic(() => import("components/color-toggle"), {
 });
 
 const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "search"];
-const expandedWidgets = ["search"];
 
 export default function Home() {
-  const { data: services, error: servicesError } = useSWR("/api/services");
-  const { data: bookmarks, error: bookmarksError } = useSWR("/api/bookmarks");
-  const { data: widgets, error: widgetsError } = useSWR("/api/widgets");
+  const { data: services } = useSWR("/api/services");
+  const { data: bookmarks } = useSWR("/api/bookmarks");
+  const { data: widgets } = useSWR("/api/widgets");
 
   return (
     <ColorProvider>
@@ -38,15 +35,15 @@ export default function Home() {
               <>
                 {widgets
                   .filter((widget) => !rightAlignedWidgets.includes(widget.type))
-                  .map((widget, i) => (
-                    <Widget key={i} widget={widget} />
+                  .map((widget) => (
+                    <Widget key={widget} widget={widget} />
                   ))}
 
                 <div className="flex flex-wrap basis-full space-x-0 sm:space-x-4 grow sm:basis-auto justify-between md:justify-end mt-2 md:mt-0">
                   {widgets
                     .filter((widget) => rightAlignedWidgets.includes(widget.type))
-                    .map((widget, i) => (
-                      <Widget key={i} widget={widget} />
+                    .map((widget) => (
+                      <Widget key={widget} widget={widget} />
                     ))}
                 </div>
               </>
diff --git a/src/utils/api-helpers.js b/src/utils/api-helpers.js
index 1cbefab8..a889487e 100644
--- a/src/utils/api-helpers.js
+++ b/src/utils/api-helpers.js
@@ -15,13 +15,13 @@ const formats = {
 };
 
 export function formatApiCall(api, args) {
-  const match = /\{.*?\}/g;
+  const find = /\{.*?\}/g;
   const replace = (match) => {
     const key = match.replace(/\{|\}/g, "");
     return args[key];
   };
 
-  return formats[api].replace(match, replace);
+  return formats[api].replace(find, replace);
 }
 
 export function formatApiUrl(widget, endpoint) {
diff --git a/src/utils/cached-fetch.js b/src/utils/cached-fetch.js
index 88372d53..22eba37f 100644
--- a/src/utils/cached-fetch.js
+++ b/src/utils/cached-fetch.js
@@ -5,9 +5,9 @@ export default async function cachedFetch(url, duration) {
 
   if (cached) {
     return cached;
-  } else {
-    const data = await fetch(url).then((res) => res.json());
-    cache.put(url, data, duration * 1000 * 60);
-    return data;
   }
+
+  const data = await fetch(url).then((res) => res.json());
+  cache.put(url, data, duration * 1000 * 60);
+  return data;
 }
diff --git a/src/utils/color-context.js b/src/utils/color-context.jsx
similarity index 75%
rename from src/utils/color-context.js
rename to src/utils/color-context.jsx
index 08063b23..83aa692d 100644
--- a/src/utils/color-context.js
+++ b/src/utils/color-context.jsx
@@ -1,4 +1,4 @@
-import { createContext, useState, useEffect } from "react";
+import { createContext, useState, useEffect, useMemo } from "react";
 
 let lastColor = false;
 
@@ -16,7 +16,7 @@ const getInitialColor = () => {
 
 export const ColorContext = createContext();
 
-export const ColorProvider = ({ initialTheme, children }) => {
+export function ColorProvider({ initialTheme, children }) {
   const [color, setColor] = useState(getInitialColor);
 
   const rawSetColor = (rawColor) => {
@@ -38,5 +38,7 @@ export const ColorProvider = ({ initialTheme, children }) => {
     rawSetColor(color);
   }, [color]);
 
-  return <ColorContext.Provider value={{ color, setColor }}>{children}</ColorContext.Provider>;
-};
+  const value = useMemo(() => ({ color, setColor }), [color]);
+
+  return <ColorContext.Provider value={value}>{children}</ColorContext.Provider>;
+}
diff --git a/src/utils/condition-map.js b/src/utils/condition-map.js
index bf99027e..f00b109c 100644
--- a/src/utils/condition-map.js
+++ b/src/utils/condition-map.js
@@ -348,7 +348,9 @@ export default function mapIcon(weatherStatusCode, timeOfDay) {
   if (mapping) {
     if (timeOfDay === "day") {
       return mapping.icon.day;
-    } else if (timeOfDay === "night") {
+    }
+
+    if (timeOfDay === "night") {
       return mapping.icon.night;
     }
   }
diff --git a/src/utils/config.js b/src/utils/config.js
index 5fa3fa7f..7b70de37 100644
--- a/src/utils/config.js
+++ b/src/utils/config.js
@@ -1,5 +1,7 @@
+/* eslint-disable no-console */
 import { join } from "path";
 import { existsSync, copyFile, promises as fs } from "fs";
+
 import yaml from "js-yaml";
 
 export default function checkAndCopyConfig(config) {
@@ -8,7 +10,7 @@ export default function checkAndCopyConfig(config) {
     const configSkeleton = join(process.cwd(), "src", "skeleton", config);
     copyFile(configSkeleton, configYaml, (err) => {
       if (err) {
-        console.log("error copying config", err);
+        console.error("error copying config", err);
         throw err;
       }
       console.info("%s was copied to the config folder", config);
diff --git a/src/utils/docker.js b/src/utils/docker.js
index 9d2d4646..0bc6b843 100644
--- a/src/utils/docker.js
+++ b/src/utils/docker.js
@@ -1,6 +1,8 @@
-import yaml from "js-yaml";
 import path from "path";
 import { promises as fs } from "fs";
+
+import yaml from "js-yaml";
+
 import checkAndCopyConfig from "utils/config";
 
 export default async function getDockerArguments(server) {
@@ -13,18 +15,21 @@ export default async function getDockerArguments(server) {
   if (!server) {
     if (process.platform !== "win32" && process.platform !== "darwin") {
       return { socketPath: "/var/run/docker.sock" };
-    } else {
-      return { host: "127.0.0.1" };
     }
-  } else if (servers[server]) {
+
+    return { host: "127.0.0.1" };
+  }
+
+  if (servers[server]) {
     if (servers[server].socket) {
       return { socketPath: servers[server].socket };
-    } else if (servers[server].host) {
-      return { host: servers[server].host, port: servers[server].port || null };
-    } else {
-      return servers[server];
     }
-  } else {
-    return null;
+
+    if (servers[server].host) {
+      return { host: servers[server].host, port: servers[server].port || null };
+    }
+
+    return servers[server];
   }
+  return null;
 }
diff --git a/src/utils/http.js b/src/utils/http.js
index 91c8c5fb..5f32f1a1 100644
--- a/src/utils/http.js
+++ b/src/utils/http.js
@@ -1,10 +1,11 @@
+/* eslint-disable prefer-promise-reject-errors */
 import https from "https";
 import http from "http";
 
 export function httpsRequest(url, params) {
-  return new Promise(function (resolve, reject) {
-    var request = https.request(url, params, function (response) {
-      var data = [];
+  return new Promise((resolve, reject) => {
+    const request = https.request(url, params, (response) => {
+      const data = [];
 
       response.on("data", (chunk) => {
         data.push(chunk);
@@ -24,9 +25,9 @@ export function httpsRequest(url, params) {
 }
 
 export function httpRequest(url, params) {
-  return new Promise(function (resolve, reject) {
-    var request = http.request(url, params, function (response) {
-      var data = [];
+  return new Promise((resolve, reject) => {
+    const request = http.request(url, params, (response) => {
+      const data = [];
 
       response.on("data", (chunk) => {
         data.push(chunk);
@@ -57,7 +58,6 @@ export function httpProxy(url, params = {}) {
       agent: httpsAgent,
       ...params,
     });
-  } else {
-    return httpRequest(constructedUrl, params);
   }
+  return httpRequest(constructedUrl, params);
 }
diff --git a/src/utils/owm-condition-map.js b/src/utils/owm-condition-map.js
index ab5658f2..79ca32f7 100644
--- a/src/utils/owm-condition-map.js
+++ b/src/utils/owm-condition-map.js
@@ -135,7 +135,7 @@ const conditions = [
       night: Icons.WiNightAltShowers,
     },
   },
- 
+
   {
     code: 500,
     icon: {
@@ -393,18 +393,17 @@ const conditions = [
       night: Icons.WiCloudy,
     },
   },
-
 ];
 
 export default function mapIcon(weatherStatusCode, timeOfDay) {
-  const mapping = conditions.find(
-    (condition) => condition.code === weatherStatusCode
-  );
+  const mapping = conditions.find((condition) => condition.code === weatherStatusCode);
 
   if (mapping) {
     if (timeOfDay === "day") {
       return mapping.icon.day;
-    } else if (timeOfDay === "night") {
+    }
+
+    if (timeOfDay === "night") {
       return mapping.icon.night;
     }
   }
diff --git a/src/utils/proxies/credentialed.js b/src/utils/proxies/credentialed.js
index 9672a759..820d8cdb 100644
--- a/src/utils/proxies/credentialed.js
+++ b/src/utils/proxies/credentialed.js
@@ -1,4 +1,4 @@
-import { getServiceWidget } from "utils/service-helpers";
+import getServiceWidget from "utils/service-helpers";
 import { formatApiCall } from "utils/api-helpers";
 import { httpProxy } from "utils/http";
 
diff --git a/src/utils/proxies/generic.js b/src/utils/proxies/generic.js
index 09eb9a0e..417d0070 100644
--- a/src/utils/proxies/generic.js
+++ b/src/utils/proxies/generic.js
@@ -1,4 +1,4 @@
-import { getServiceWidget } from "utils/service-helpers";
+import getServiceWidget from "utils/service-helpers";
 import { formatApiCall } from "utils/api-helpers";
 import { httpProxy } from "utils/http";
 
diff --git a/src/utils/proxies/npm.js b/src/utils/proxies/npm.js
index d6c68cd8..d60cedd1 100644
--- a/src/utils/proxies/npm.js
+++ b/src/utils/proxies/npm.js
@@ -1,4 +1,4 @@
-import { getServiceWidget } from "utils/service-helpers";
+import getServiceWidget from "utils/service-helpers";
 import { formatApiCall } from "utils/api-helpers";
 
 export default async function npmProxyHandler(req, res) {
@@ -25,7 +25,7 @@ export default async function npmProxyHandler(req, res) {
         method: "GET",
         headers: {
           "Content-Type": "application/json",
-          Authorization: "Bearer " + authResponse.token,
+          Authorization: `Bearer ${authResponse.token}`,
         },
       }).then((response) => response.json());
 
diff --git a/src/utils/proxies/nzbget.js b/src/utils/proxies/nzbget.js
index 7bf078b4..7f0a450d 100644
--- a/src/utils/proxies/nzbget.js
+++ b/src/utils/proxies/nzbget.js
@@ -1,5 +1,6 @@
 import { JSONRPCClient } from "json-rpc-2.0";
-import { getServiceWidget } from "utils/service-helpers";
+
+import getServiceWidget from "utils/service-helpers";
 
 export default async function nzbgetProxyHandler(req, res) {
   const { group, service, endpoint } = req.query;
@@ -25,9 +26,9 @@ export default async function nzbgetProxyHandler(req, res) {
           if (response.status === 200) {
             const jsonRPCResponse = await response.json();
             return client.receive(jsonRPCResponse);
-          } else if (jsonRPCRequest.id !== undefined) {
-            return Promise.reject(new Error(response.statusText));
           }
+
+          return Promise.reject(new Error(response.statusText));
         })
       );
 
diff --git a/src/utils/proxies/rutorrent.js b/src/utils/proxies/rutorrent.js
index 3f9dda3c..78e7b815 100644
--- a/src/utils/proxies/rutorrent.js
+++ b/src/utils/proxies/rutorrent.js
@@ -1,6 +1,6 @@
 import RuTorrent from "rutorrent-promise";
 
-import { getServiceWidget } from "utils/service-helpers";
+import getServiceWidget from "utils/service-helpers";
 
 export default async function rutorrentProxyHandler(req, res) {
   const { group, service } = req.query;
diff --git a/src/utils/service-helpers.js b/src/utils/service-helpers.js
index 986fc227..f17434f4 100644
--- a/src/utils/service-helpers.js
+++ b/src/utils/service-helpers.js
@@ -1,24 +1,21 @@
 import { promises as fs } from "fs";
 import path from "path";
+
 import yaml from "js-yaml";
 
-export async function getServiceWidget(group, service) {
+export default async function getServiceWidget(group, service) {
   const servicesYaml = path.join(process.cwd(), "config", "services.yaml");
   const fileContents = await fs.readFile(servicesYaml, "utf8");
   const services = yaml.load(fileContents);
 
   // map easy to write YAML objects into easy to consume JS arrays
-  const servicesArray = services.map((group) => {
-    return {
-      name: Object.keys(group)[0],
-      services: group[Object.keys(group)[0]].map((entries) => {
-        return {
-          name: Object.keys(entries)[0],
-          ...entries[Object.keys(entries)[0]],
-        };
-      }),
-    };
-  });
+  const servicesArray = services.map((servicesGroup) => ({
+    name: Object.keys(servicesGroup)[0],
+    services: servicesGroup[Object.keys(servicesGroup)[0]].map((entries) => ({
+      name: Object.keys(entries)[0],
+      ...entries[Object.keys(entries)[0]],
+    })),
+  }));
 
   const serviceGroup = servicesArray.find((g) => g.name === group);
   if (serviceGroup) {
diff --git a/src/utils/stats-helpers.js b/src/utils/stats-helpers.js
index b98f1cd7..6d4ee7ad 100644
--- a/src/utils/stats-helpers.js
+++ b/src/utils/stats-helpers.js
@@ -19,7 +19,7 @@ export function formatBytes(bytes, decimals = 2) {
 
   const i = Math.floor(Math.log(bytes) / Math.log(k));
 
-  return parseFloat(bytes / Math.pow(k, i)).toFixed(dm) + " " + sizes[i];
+  return `${parseFloat(bytes / k ** i).toFixed(dm)} ${sizes[i]}`;
 }
 
 export function formatBits(bytes, decimals = 2) {
@@ -31,5 +31,5 @@ export function formatBits(bytes, decimals = 2) {
 
   const i = Math.floor(Math.log(bytes) / Math.log(k));
 
-  return parseFloat(bytes / Math.pow(k, i)).toFixed(dm) + " " + sizes[i];
+  return `${parseFloat(bytes / k ** i).toFixed(dm)} ${sizes[i]}`;
 }
diff --git a/src/utils/theme-context.js b/src/utils/theme-context.jsx
similarity index 76%
rename from src/utils/theme-context.js
rename to src/utils/theme-context.jsx
index 476fcb8d..89b5a57b 100644
--- a/src/utils/theme-context.js
+++ b/src/utils/theme-context.jsx
@@ -1,4 +1,4 @@
-import { createContext, useState, useEffect } from "react";
+import { createContext, useState, useEffect, useMemo } from "react";
 
 const getInitialTheme = () => {
   if (typeof window !== "undefined" && window.localStorage) {
@@ -18,7 +18,7 @@ const getInitialTheme = () => {
 
 export const ThemeContext = createContext();
 
-export const ThemeProvider = ({ initialTheme, children }) => {
+export function ThemeProvider({ initialTheme, children }) {
   const [theme, setTheme] = useState(getInitialTheme);
 
   const rawSetTheme = (rawTheme) => {
@@ -39,5 +39,7 @@ export const ThemeProvider = ({ initialTheme, children }) => {
     rawSetTheme(theme);
   }, [theme]);
 
-  return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
-};
+  const value = useMemo(() => ({ theme, setTheme }), [theme]);
+
+  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
+}