Repository: strudel-science/strudel-kit (language: python)

Issue:
Add automatic file-based routing



Discussion / hints:

Fix patch:
diff --git a/strudel-demo-app/package-lock.json b/strudel-demo-app/package-lock.json
index a9edb9e4..e544ac3e 100644
--- a/strudel-demo-app/package-lock.json
+++ b/strudel-demo-app/package-lock.json
@@ -1,17 +1,17 @@
 {
-  "//": "strudel-kit-variable-next-line",
   "name": "strudel-demo-app",
   "version": "0.0.0",
   "lockfileVersion": 3,
   "requires": true,
+  "//": "strudel-kit-variable-next-line",
   "packages": {
     "": {
-      "//": "strudel-kit-variable-next-line",
       "name": "strudel-demo-app",
       "version": "0.0.0",
       "dependencies": {
         "@emotion/react": "^11.11.4",
         "@emotion/styled": "^11.11.0",
+        "@generouted/react-router": "^1.18.8",
         "@mui/icons-material": "^5.15.14",
         "@mui/lab": "^5.0.0-alpha.169",
         "@mui/material": "^5.15.14",
@@ -588,7 +588,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "aix"
@@ -604,7 +603,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -620,7 +618,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -636,7 +633,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -652,7 +648,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -668,7 +663,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -684,7 +678,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "freebsd"
@@ -700,7 +693,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "freebsd"
@@ -716,7 +708,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -732,7 +723,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -748,7 +738,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -764,7 +753,6 @@
       "cpu": [
         "loong64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -780,7 +768,6 @@
       "cpu": [
         "mips64el"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -796,7 +783,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -812,7 +798,6 @@
       "cpu": [
         "riscv64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -828,7 +813,6 @@
       "cpu": [
         "s390x"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -844,7 +828,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -860,7 +843,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "netbsd"
@@ -876,7 +858,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "openbsd"
@@ -892,7 +873,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "sunos"
@@ -908,7 +888,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -924,7 +903,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -940,7 +918,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -1076,6 +1053,20 @@
       "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
       "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
     },
+    "node_modules/@generouted/react-router": {
+      "version": "1.18.8",
+      "resolved": "https://registry.npmjs.org/@generouted/react-router/-/react-router-1.18.8.tgz",
+      "integrity": "sha512-LN1mIF1Akj7t3mvTeDlBZCa8a0wFPooLHDKam9YcG/UcgOEMTn+Y63tV5AGdcM33xtOYeE+pTX+ud+GR3yNe0g==",
+      "dependencies": {
+        "fast-glob": "^3.3.2",
+        "generouted": "^1.18.8"
+      },
+      "peerDependencies": {
+        "react": ">=18",
+        "react-router-dom": ">=6",
+        "vite": ">=4"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.14",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -1622,7 +1613,6 @@
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
       "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "2.0.5",
         "run-parallel": "^1.1.9"
@@ -1635,7 +1625,6 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
       "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true,
       "engines": {
         "node": ">= 8"
       }
@@ -1644,7 +1633,6 @@
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
       "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.scandir": "2.1.5",
         "fastq": "^1.6.0"
@@ -1752,7 +1740,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -1765,7 +1752,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -1778,7 +1764,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -1791,7 +1776,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -1804,7 +1788,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1817,7 +1800,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1830,7 +1812,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1843,7 +1824,6 @@
       "cpu": [
         "ppc64le"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1856,7 +1836,6 @@
       "cpu": [
         "riscv64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1869,7 +1848,6 @@
       "cpu": [
         "s390x"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1882,7 +1860,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1895,7 +1872,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -1908,7 +1884,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -1921,7 +1896,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -1934,7 +1908,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -2054,8 +2027,7 @@
     "node_modules/@types/estree": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
-      "dev": true
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
     },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
@@ -2514,7 +2486,6 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
       "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
       "dependencies": {
         "fill-range": "^7.0.1"
       },
@@ -3200,7 +3171,6 @@
       "version": "0.20.2",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
       "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
-      "dev": true,
       "hasInstallScript": true,
       "bin": {
         "esbuild": "bin/esbuild"
@@ -3644,7 +3614,6 @@
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
       "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
         "@nodelib/fs.walk": "^1.2.3",
@@ -3660,7 +3629,6 @@
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
       "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
       "dependencies": {
         "is-glob": "^4.0.1"
       },
@@ -3692,7 +3660,6 @@
       "version": "1.17.1",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
       "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
-      "dev": true,
       "dependencies": {
         "reusify": "^1.0.4"
       }
@@ -3713,7 +3680,6 @@
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
       "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -3805,7 +3771,6 @@
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
       "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "dev": true,
       "hasInstallScript": true,
       "optional": true,
       "os": [
@@ -3823,6 +3788,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/generouted": {
+      "version": "1.18.8",
+      "resolved": "https://registry.npmjs.org/generouted/-/generouted-1.18.8.tgz",
+      "integrity": "sha512-iaKHLtR/ziZEJT+0n0+6RMTRFSkPsnF7GvnDvM5xYOjKvFjFZO/u5HRQpKIlBhNC0IGu6JSdHlcu9EJcLOQXvA==",
+      "peerDependencies": {
+        "vite": ">=3"
+      }
+    },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4337,7 +4310,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
       "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -4365,7 +4337,6 @@
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
       "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
       "dependencies": {
         "is-extglob": "^2.1.1"
       },
@@ -4390,7 +4361,6 @@
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
       "engines": {
         "node": ">=0.12.0"
       }
@@ -4638,7 +4608,6 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
       "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
       "engines": {
         "node": ">= 8"
       }
@@ -4647,7 +4616,6 @@
       "version": "4.0.5",
       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
       "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
-      "dev": true,
       "dependencies": {
         "braces": "^3.0.2",
         "picomatch": "^2.3.1"
@@ -4730,7 +4698,6 @@
       "version": "3.3.7",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
       "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5002,7 +4969,6 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
       "engines": {
         "node": ">=8.6"
       },
@@ -5080,7 +5046,6 @@
       "version": "8.4.38",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
       "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
-      "dev": true,
       "funding": [
         {
           "type": "opencollective",
@@ -5166,7 +5131,6 @@
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
       "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5434,7 +5398,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
       "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
       "engines": {
         "iojs": ">=1.0.0",
         "node": ">=0.10.0"
@@ -5464,7 +5427,6 @@
       "version": "4.13.2",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz",
       "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==",
-      "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
       },
@@ -5498,7 +5460,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
       "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -5644,7 +5605,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
       "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -5867,7 +5827,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
       "dependencies": {
         "is-number": "^7.0.0"
       },
@@ -6019,7 +5978,6 @@
       "version": "5.2.6",
       "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
       "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==",
-      "dev": true,
       "dependencies": {
         "esbuild": "^0.20.1",
         "postcss": "^8.4.36",
diff --git a/strudel-demo-app/package.json b/strudel-demo-app/package.json
index 5c6b37be..a948d8bc 100644
--- a/strudel-demo-app/package.json
+++ b/strudel-demo-app/package.json
@@ -13,6 +13,7 @@
   "dependencies": {
     "@emotion/react": "^11.11.4",
     "@emotion/styled": "^11.11.0",
+    "@generouted/react-router": "^1.18.8",
     "@mui/icons-material": "^5.15.14",
     "@mui/lab": "^5.0.0-alpha.169",
     "@mui/material": "^5.15.14",
diff --git a/strudel-demo-app/src/app/App.tsx b/strudel-demo-app/src/App.tsx
similarity index 75%
rename from strudel-demo-app/src/app/App.tsx
rename to strudel-demo-app/src/App.tsx
index f004997f..6763697b 100644
--- a/strudel-demo-app/src/app/App.tsx
+++ b/strudel-demo-app/src/App.tsx
@@ -1,13 +1,12 @@
 import React from 'react';
 import { ThemeProvider } from '@mui/material/styles';
 import { CssBaseline } from '@mui/material';
-import { RouterProvider } from 'react-router-dom';
 import { LocalizationProvider } from '@mui/x-date-pickers';
 import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
-import { router } from './routes';
+import { Routes } from '@generouted/react-router'
 import { theme } from './theme';
-import { AppProvider } from '../context/ContextProvider';
-import { ApiModal } from '../components/ApiModal';
+import { AppProvider } from './context/ContextProvider';
+import { ApiModal } from './components/ApiModal';
 
 export const basename = document.querySelector('base')?.getAttribute('href') ?? '';
 
@@ -18,7 +17,7 @@ const App: React.FC = () => {
         <CssBaseline />
         {/* strudel-kit-variable-next-line */}
         <AppProvider appTitle="Demo App">
-          <RouterProvider router={router} />
+          <Routes />
           <ApiModal />
         </AppProvider>
       </ThemeProvider>
diff --git a/strudel-demo-app/src/app/compare-data/README.md b/strudel-demo-app/src/app/compare-data/README.md
deleted file mode 100644
index 32dff9d0..00000000
--- a/strudel-demo-app/src/app/compare-data/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Use this Task Flow
-
-Install the STRUDEL CLI tool:
-
-```
-pip install strudel-cli
-```
-
-Create a base app:
-
-```
-strudel create-app my-app
-```
-
-Go to the root directory of your new app:
-
-```
-cd my-app
-```
-
-Add the task flow to your app:
-
-```
-strudel add-taskflow my-compare-data-tf --template compare-data
-```
-
-Accept the default options or create a configuration file for your Task Flow.
-
-```
-strudel add-taskflow --config my-config.json
-```
-
-### [How to Configure this Task Flow](https://github.com/strudel-science/strudel-kit/blob/main/docs/task-flows/compare-data/config.md)
-
-## Learn More
-
-[Read more about the strudel-cli and learn advanced usage.](https://github.com/strudel-science/strudel-kit/tree/main/docs)
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/contribute-data/README.md b/strudel-demo-app/src/app/contribute-data/README.md
deleted file mode 100644
index db94b9ba..00000000
--- a/strudel-demo-app/src/app/contribute-data/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Use this Task Flow
-
-Install the STRUDEL CLI tool:
-
-```
-pip install strudel-cli
-```
-
-Create a base app:
-
-```
-strudel create-app my-app
-```
-
-Go to the root directory of your new app:
-
-```
-cd my-app
-```
-
-Add the task flow to your app:
-
-```
-strudel add-taskflow my-contribute-data-tf --template contribute-data
-```
-
-Accept the default options or create a configuration file for your Task Flow.
-
-```
-strudel add-taskflow --config my-config.json
-```
-
-### [How to Configure this Task Flow](https://github.com/strudel-science/strudel-kit/blob/main/docs/task-flows/contribute-data/config.md)
-
-## Learn More
-
-[Read more about the strudel-cli and learn advanced usage.](https://github.com/strudel-science/strudel-kit/tree/main/docs)
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/explore-data/README.md b/strudel-demo-app/src/app/explore-data/README.md
deleted file mode 100644
index 916ee249..00000000
--- a/strudel-demo-app/src/app/explore-data/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Use this Task Flow
-
-Install the STRUDEL CLI tool:
-
-```
-pip install strudel-cli
-```
-
-Create a base app:
-
-```
-strudel create-app my-app
-```
-
-Go to the root directory of your new app:
-
-```
-cd my-app
-```
-
-Add the task flow to your app:
-
-```
-strudel add-taskflow my-explore-data-tf --template explore-data
-```
-
-Accept the default options or create a configuration file for your Task Flow.
-
-```
-strudel add-taskflow --config my-config.json
-```
-
-### [How to Configure this Task Flow](https://github.com/strudel-science/strudel-kit/blob/main/docs/task-flows/explore-data/config.md)
-
-## Learn More
-
-[Read more about the strudel-cli and learn advanced usage.](https://github.com/strudel-science/strudel-kit/tree/main/docs)
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/explore-data/index.tsx b/strudel-demo-app/src/app/explore-data/index.tsx
deleted file mode 100644
index d4ea4b29..00000000
--- a/strudel-demo-app/src/app/explore-data/index.tsx
+++ /dev/null
@@ -1,1 +0,0 @@
-export { ExploreDataWrapper } from './ExploreDataWrapper';
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/home/HomePage.tsx b/strudel-demo-app/src/app/home/HomePage.tsx
deleted file mode 100644
index 8f6a7b6a..00000000
--- a/strudel-demo-app/src/app/home/HomePage.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import { Box, Container, Paper } from '@mui/material';
-import { TopBar } from '../../components/TopBar';
-
-/**
- * TODO:
- * - Add instructions about how to rig up the router for task flows
- * - Add instructions for putting dataset into app
- */
-export const HomePage: React.FC = () => {
-  return (
-    <Box>
-      <TopBar />
-      <Container
-        maxWidth="md"
-        sx={{
-          marginTop: 3,
-          marginBottom: 3,
-        }}
-      >
-        <Paper
-          sx={{
-            padding: 2
-          }}
-        >
-          Welcome to your STRUDEL app!
-        </Paper>
-      </Container>
-    </Box>
-  )
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/home/TaskFlowsPage.tsx b/strudel-demo-app/src/app/home/TaskFlowsPage.tsx
deleted file mode 100644
index 5f7c790e..00000000
--- a/strudel-demo-app/src/app/home/TaskFlowsPage.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React, { PropsWithChildren } from 'react';
-import { Link, Container, Paper, List, ListItem, Typography, Box } from '@mui/material';
-import { Link as RouterLink } from 'react-router-dom';
-import { TopBar } from '../../components/TopBar';
-
-export const TaskFlowsPage: React.FC = () => {
-  return (
-    <Box>
-      <TopBar />
-      <Container
-        maxWidth="md"
-        sx={{
-          marginTop: 3,
-          marginBottom: 3,
-        }}
-      >
-        <Paper
-          sx={{
-            padding: 2
-          }}
-        >
-          <Box
-            component="aside"
-            sx={{ 
-              position: 'relative', 
-              width: '250px' 
-            }}
-          >
-            <List>
-              <NavListItem>
-                <Link component={RouterLink} to="compare-data">
-                  Compare Data
-                </Link>
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="contribute-data">
-                  Contribute Data
-                </Link>
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="explore-data">
-                  Explore Data
-                </Link>
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="monitor-activities">
-                  Monitor Activities
-                </Link> 
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="run-computation">
-                  Run Computation
-                </Link> 
-              </NavListItem>
-              <NavListItem>
-                <Link component={RouterLink} to="search-data-repositories">
-                  Search Data Repositories
-                </Link>
-              </NavListItem>
-            </List>
-          </Box>
-        </Paper>
-      </Container>
-    </Box>
-  )
-}
-
-const NavListItem: React.FC<PropsWithChildren> = ({ children }) => {
-  return (
-    <ListItem
-      sx={{
-        padding: 0,
-      }}
-    >
-      <Typography
-        sx={{
-          padding: '0.5rem 1rem',
-          color: 'neutral.main',
-          fontSize: '1.125rem',
-          fontWeight: 'bold',
-        }}
-      >
-        {children}
-      </Typography>
-    </ListItem>
-  )
-}
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/monitor-activities/README.md b/strudel-demo-app/src/app/monitor-activities/README.md
deleted file mode 100644
index 82da4770..00000000
--- a/strudel-demo-app/src/app/monitor-activities/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Use this Task Flow
-
-Install the STRUDEL CLI tool:
-
-```
-pip install strudel-cli
-```
-
-Create a base app:
-
-```
-strudel create-app my-app
-```
-
-Go to the root directory of your new app:
-
-```
-cd my-app
-```
-
-Add the task flow to your app:
-
-```
-strudel add-taskflow my-monitor-activities-tf --template monitor-activities
-```
-
-Accept the default options or create a configuration file for your Task Flow.
-
-```
-strudel add-taskflow --config my-config.json
-```
-
-### [How to Configure this Task Flow](https://github.com/strudel-science/strudel-kit/blob/main/docs/task-flows/monitor-activities/config.md)
-
-## Learn More
-
-[Read more about the strudel-cli and learn advanced usage.](https://github.com/strudel-science/strudel-kit/tree/main/docs)
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/routes.tsx b/strudel-demo-app/src/app/routes.tsx
deleted file mode 100644
index 59cd9849..00000000
--- a/strudel-demo-app/src/app/routes.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-import { createHashRouter, Navigate } from "react-router-dom";
-import { CompareDataWrapper } from "./compare-data/CompareDataWrapper";
-import { NewScenario } from "./compare-data/NewScenario";
-import { ScenarioComparison } from "./compare-data/ScenarioComparison";
-import { ScenarioList } from "./compare-data/ScenarioList";
-import { ContributeDataWrapper } from "./contribute-data/ContributeDataWrapper";
-import { ContributorPortal } from "./contribute-data/ContributorPortal";
-import { NewDataset } from "./contribute-data/NewDataset";
-import { Register } from "./contribute-data/Register";
-import { ReviewDataset } from "./contribute-data/ReviewDataset";
-import { ExploreDataWrapper } from "./explore-data";
-import { DataDetailPage } from "./explore-data/DataDetailPage";
-import { DataExplorer } from "./explore-data/DataExplorer";
-import { TaskFlowsPage } from "./home/TaskFlowsPage";
-import { ActivityDetail } from "./monitor-activities/ActivityDetail";
-import { ActivityList } from "./monitor-activities/ActivityList";
-import { MonitorActivitiesWrapper } from "./monitor-activities/MonitorActivitiesWrapper";
-import { RunComputationWrapper } from "./run-computation";
-import { DataInputs } from "./run-computation/DataInputs";
-import { Settings } from "./run-computation/Settings";
-import { Results } from "./run-computation/Results";
-import { RunningComputation } from "./run-computation/RunningComputation";
-import { Scenario } from "./run-computation/Scenario";
-import { DatasetDetail } from "./search-data-repositories/DatasetDetail";
-import { DatasetExplorer } from "./search-data-repositories/DatasetExplorer";
-import { SearchDataRepositoriesWrapper } from "./search-data-repositories/SearchDataRepositoriesWrapper";
-import { ComputationsList } from "./run-computation/ComputationsList";
-
-/**
- * App Router
- * Registers URL paths to page components.
- */
-export const router = createHashRouter([
-  {
-    path: "/",
-    element: <TaskFlowsPage />,
-  },
-  {
-    path: "/search-data-repositories",
-    element: <SearchDataRepositoriesWrapper />,
-    children: [
-      {
-        index: true,
-        element: <DatasetExplorer />
-      },
-      {
-        path: ':datasetId',
-        element: <DatasetDetail />
-      }
-    ]
-  },
-  {
-    path: "/explore-data",
-    element: <ExploreDataWrapper />,
-    children: [
-      {
-        index: true,
-        element: <DataExplorer />
-      },
-      {
-        path: ':datasetId',
-        element: <DataDetailPage />
-      }
-    ]
-  },
-  {
-    path: "/run-computation",
-    element: <RunComputationWrapper />,
-    children: [
-      {
-        index: true,
-        element: <ComputationsList />
-      },
-      {
-        path: "scenario",
-        element: <Scenario />,
-        children: [
-          {
-            path: 'data-inputs',
-            element: <DataInputs />
-          },
-          {
-            path: 'settings',
-            element: <Settings />
-          },
-          {
-            path: 'running',
-            element: <RunningComputation />
-          },
-          {
-            path: 'results',
-            element: <Results />
-          }
-        ]
-      },
-    ]
-  },
-  {
-    path: "/contribute-data",
-    element: <ContributeDataWrapper />,
-    children: [
-      {
-        index: true,
-        element: <Register />
-      },
-      {
-        path: 'register',
-        element: <Register />
-      },
-      {
-        path: 'portal',
-        element: <ContributorPortal />
-      },
-      {
-        path: 'new',
-        element: <NewDataset />
-      },
-      {
-        path: 'review',
-        element: <ReviewDataset />
-      }
-    ]
-  },
-  {
-    path: "/monitor-activities",
-    element: <MonitorActivitiesWrapper />,
-    children: [
-      // {
-      //   index: true,
-      //   element: <ActivityCalendar />
-      // },
-      {
-        index: true,
-        element: <ActivityList />,
-      },
-      {
-        path: 'list',
-        children: [
-          {
-            index: true,
-            element: <ActivityList />,
-          },
-          {
-            path: 'detail',
-            element: <ActivityDetail />
-          },
-        ]
-      },
-    ]
-  },
-  {
-    path: "/compare-data",
-    element: <CompareDataWrapper />,
-    children: [
-      {
-        index: true,
-        element: <ScenarioList />
-      },
-      {
-        path: 'compare',
-        element: <ScenarioComparison />
-      },
-      {
-        path: 'new',
-        element: <NewScenario />
-      },
-    ]
-  },
-  {
-    path: '*',
-    element: <Navigate to="/" replace />
-  }
-]);
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/README.md b/strudel-demo-app/src/app/run-computation/README.md
deleted file mode 100644
index 049d510e..00000000
--- a/strudel-demo-app/src/app/run-computation/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Use this Task Flow
-
-Install the STRUDEL CLI tool:
-
-```
-pip install strudel-cli
-```
-
-Create a base app:
-
-```
-strudel create-app my-app
-```
-
-Go to the root directory of your new app:
-
-```
-cd my-app
-```
-
-Add the task flow to your app:
-
-```
-strudel add-taskflow my-run-computation-tf --template run-computation
-```
-
-Accept the default options or create a configuration file for your Task Flow.
-
-```
-strudel add-taskflow --config my-config.json
-```
-
-### [How to Configure this Task Flow](https://github.com/strudel-science/strudel-kit/blob/main/docs/task-flows/run-computation/config.md)
-
-## Learn More
-
-[Read more about the strudel-cli and learn advanced usage.](https://github.com/strudel-science/strudel-kit/tree/main/docs)
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/index.tsx b/strudel-demo-app/src/app/run-computation/index.tsx
deleted file mode 100644
index 7c56f7c6..00000000
--- a/strudel-demo-app/src/app/run-computation/index.tsx
+++ /dev/null
@@ -1,1 +0,0 @@
-export { RunComputationWrapper } from './RunComputationWrapper';
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/search-data-repositories/README.md b/strudel-demo-app/src/app/search-data-repositories/README.md
deleted file mode 100644
index 1f8c1afd..00000000
--- a/strudel-demo-app/src/app/search-data-repositories/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Use this Task Flow
-
-Install the STRUDEL CLI tool:
-
-```
-pip install strudel-cli
-```
-
-Create a base app:
-
-```
-strudel create-app my-app
-```
-
-Go to the root directory of your new app:
-
-```
-cd my-app
-```
-
-Add the task flow to your app:
-
-```
-strudel add-taskflow my-search-data-repositories-tf --template csearch-data-repositories
-```
-
-Accept the default options or create a configuration file for your Task Flow.
-
-```
-strudel add-taskflow --config my-config.json
-```
-
-### [How to Configure this Task Flow](https://github.com/strudel-science/strudel-kit/blob/main/docs/task-flows/search-data-repositories/config.md)
-
-## Learn More
-
-[Read more about the strudel-cli and learn advanced usage.](https://github.com/strudel-science/strudel-kit/tree/main/docs)
\ No newline at end of file
diff --git a/strudel-demo-app/src/components/TopBar.tsx b/strudel-demo-app/src/components/TopBar.tsx
index 74ecbd53..8fd470f1 100644
--- a/strudel-demo-app/src/components/TopBar.tsx
+++ b/strudel-demo-app/src/components/TopBar.tsx
@@ -4,12 +4,14 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
 import React from 'react';
 import { useAppState } from '../context/ContextProvider';
 import { AppLink } from './AppLink';
+import { routes } from '@generouted/react-router';
 
 /**
  * Top navigation bar component
  */
 export const TopBar: React.FC = () => {
   const app = useAppState();
+  console.log(routes);
   return (
     <AppBar color="default" position="static">
       <Toolbar>
diff --git a/strudel-demo-app/src/main.tsx b/strudel-demo-app/src/main.tsx
index be1c6f0c..990f430b 100644
--- a/strudel-demo-app/src/main.tsx
+++ b/strudel-demo-app/src/main.tsx
@@ -1,7 +1,9 @@
 import React from 'react'
 import ReactDOM from 'react-dom/client'
 import './index.css'
-import App from './app/App'
+import App from './App'
+
+export const basename = document.querySelector('base')?.getAttribute('href') ?? '';
 
 ReactDOM.createRoot(document.getElementById('root')!).render(
   <React.StrictMode>
diff --git a/strudel-demo-app/src/app/compare-data/definitions.json b/strudel-demo-app/src/pages/compare-data/_config/definitions.json
similarity index 100%
rename from strudel-demo-app/src/app/compare-data/definitions.json
rename to strudel-demo-app/src/pages/compare-data/_config/definitions.json
diff --git a/strudel-demo-app/src/app/compare-data/context/ContextProvider.tsx b/strudel-demo-app/src/pages/compare-data/_context/ContextProvider.tsx
similarity index 96%
rename from strudel-demo-app/src/app/compare-data/context/ContextProvider.tsx
rename to strudel-demo-app/src/pages/compare-data/_context/ContextProvider.tsx
index abd3b93b..3e3bd237 100644
--- a/strudel-demo-app/src/app/compare-data/context/ContextProvider.tsx
+++ b/strudel-demo-app/src/pages/compare-data/_context/ContextProvider.tsx
@@ -1,6 +1,10 @@
 import React, { useEffect, useReducer, useContext } from 'react';
 import { CompareDataAction, CompareDataActionType, setComparisonData, setData } from './actions';
-import { CompareDataGridColDef } from '../CompareDataWrapper';
+import { GridColDef } from '@mui/x-data-grid';
+
+export type CompareDataGridColDef = GridColDef & {
+  isComparisonMetric?: boolean;
+}
 
 interface ComparisonRow {
   [key: string]: number | string | null | undefined;
diff --git a/strudel-demo-app/src/app/compare-data/context/actions.ts b/strudel-demo-app/src/pages/compare-data/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/app/compare-data/context/actions.ts
rename to strudel-demo-app/src/pages/compare-data/_context/actions.ts
diff --git a/strudel-demo-app/src/app/compare-data/CompareDataWrapper.tsx b/strudel-demo-app/src/pages/compare-data/_layout.tsx
similarity index 73%
rename from strudel-demo-app/src/app/compare-data/CompareDataWrapper.tsx
rename to strudel-demo-app/src/pages/compare-data/_layout.tsx
index 8d4511a1..7cbd8ece 100644
--- a/strudel-demo-app/src/app/compare-data/CompareDataWrapper.tsx
+++ b/strudel-demo-app/src/pages/compare-data/_layout.tsx
@@ -1,18 +1,17 @@
 import { Box } from '@mui/material';
-import { GridColDef } from '@mui/x-data-grid';
 import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { CompareDataProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { CompareDataProvider } from './_context/ContextProvider';
+import definitions from './_config/definitions.json';
+import { basename } from '../../main';
 
 /**
  * Top-level wrapper for the compare-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const CompareDataWrapper: React.FC = () => {
+const CompareDataWrapper: React.FC = () => {
   // strudel-kit-variable-next-line
   const scenarios = useDataFromSource('default/compare-data/scenarios.json', basename);
 
@@ -38,6 +37,4 @@ export const CompareDataWrapper: React.FC = () => {
   )
 }
 
-export type CompareDataGridColDef = GridColDef & {
-  isComparisonMetric?: boolean;
-}
\ No newline at end of file
+export default CompareDataWrapper;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/compare-data/ScenarioComparison.tsx b/strudel-demo-app/src/pages/compare-data/compare.tsx
similarity index 93%
rename from strudel-demo-app/src/app/compare-data/ScenarioComparison.tsx
rename to strudel-demo-app/src/pages/compare-data/compare.tsx
index 8761a936..27e92095 100644
--- a/strudel-demo-app/src/app/compare-data/ScenarioComparison.tsx
+++ b/strudel-demo-app/src/pages/compare-data/compare.tsx
@@ -3,8 +3,8 @@ import { Box, Button, Container, Link, Paper, Stack } from '@mui/material';
 import React, { useEffect } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
-import { useCompareData } from './context/ContextProvider';
-import { setComparing } from './context/actions';
+import { useCompareData } from './_context/ContextProvider';
+import { setComparing } from './_context/actions';
 import { DataGrid } from '@mui/x-data-grid';
 
 /**
@@ -12,7 +12,7 @@ import { DataGrid } from '@mui/x-data-grid';
  * Displays a table with the selected items from `<ScenarioList>`
  * as the columns and the metrics as the rows.
  */
-export const ScenarioComparison: React.FC = () => {
+const ScenarioComparison: React.FC = () => {
   const { state, dispatch } = useCompareData();
 
   /**
@@ -94,4 +94,6 @@ export const ScenarioComparison: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+export default ScenarioComparison;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/compare-data/ScenarioList.tsx b/strudel-demo-app/src/pages/compare-data/index.tsx
similarity index 89%
rename from strudel-demo-app/src/app/compare-data/ScenarioList.tsx
rename to strudel-demo-app/src/pages/compare-data/index.tsx
index c2dacb11..0a8087c5 100644
--- a/strudel-demo-app/src/app/compare-data/ScenarioList.tsx
+++ b/strudel-demo-app/src/pages/compare-data/index.tsx
@@ -4,15 +4,15 @@ import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
 import { PageHeader } from '../../components/PageHeader';
-import { useCompareData } from './context/ContextProvider';
-import { setSelectedRows } from './context/actions';
+import { useCompareData } from './_context/ContextProvider';
+import { setSelectedRows } from './_context/actions';
   
 /**
  * List page to show comparable items in the compare-data Task Flow.
  * Items in this table are selectable and can be sent to the `<ScenarioComparison>` 
  * page to be rendered in the comparison table. 
  */
-export const ScenarioList: React.FC = () => {
+const ScenarioList: React.FC = () => {
   const { state, dispatch } = useCompareData();
   
   /**
@@ -97,4 +97,13 @@ export const ScenarioList: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+// const ScenarioList: React.FC = () => {
+//   const { state, dispatch } = useCompareData();
+//   return (
+//     <h1>Compare Data List</h1>
+//   )
+// }
+
+export default ScenarioList;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/compare-data/NewScenario.tsx b/strudel-demo-app/src/pages/compare-data/new.tsx
similarity index 93%
rename from strudel-demo-app/src/app/compare-data/NewScenario.tsx
rename to strudel-demo-app/src/pages/compare-data/new.tsx
index 5b5bf21b..1180b0f1 100644
--- a/strudel-demo-app/src/app/compare-data/NewScenario.tsx
+++ b/strudel-demo-app/src/pages/compare-data/new.tsx
@@ -2,8 +2,8 @@ import { Box, Button, Container, Link, Paper, Stack } from '@mui/material';
 import React, { useEffect } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
-import { useCompareData } from './context/ContextProvider';
-import { setComparing } from './context/actions';
+import { useCompareData } from './_context/ContextProvider';
+import { setComparing } from './_context/actions';
 
 /**
  * Page for filling out a form for adding a new item to 
@@ -74,4 +74,6 @@ export const NewScenario: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+export default NewScenario;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/contribute-data/DataFilesPanel.tsx b/strudel-demo-app/src/pages/contribute-data/_components/DataFilesPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/app/contribute-data/DataFilesPanel.tsx
rename to strudel-demo-app/src/pages/contribute-data/_components/DataFilesPanel.tsx
diff --git a/strudel-demo-app/src/app/contribute-data/DatasetView.tsx b/strudel-demo-app/src/pages/contribute-data/_components/DatasetView.tsx
similarity index 97%
rename from strudel-demo-app/src/app/contribute-data/DatasetView.tsx
rename to strudel-demo-app/src/pages/contribute-data/_components/DatasetView.tsx
index abf0e98b..47a48b1a 100644
--- a/strudel-demo-app/src/app/contribute-data/DatasetView.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/_components/DatasetView.tsx
@@ -2,7 +2,7 @@ import { Box, Paper, Stack, Typography } from '@mui/material';
 import { GridColDef } from '@mui/x-data-grid';
 import React from 'react';
 import { DataGrid } from '@mui/x-data-grid';
-import { LabelValueTable } from '../../components/LabelValueTable';
+import { LabelValueTable } from '../../../components/LabelValueTable';
 import { files } from './DataFilesPanel';
 
 /**
diff --git a/strudel-demo-app/src/app/contribute-data/MetadataPanel.tsx b/strudel-demo-app/src/pages/contribute-data/_components/MetadataPanel.tsx
similarity index 100%
rename from strudel-demo-app/src/app/contribute-data/MetadataPanel.tsx
rename to strudel-demo-app/src/pages/contribute-data/_components/MetadataPanel.tsx
diff --git a/strudel-demo-app/src/app/contribute-data/ValidationChecks.tsx b/strudel-demo-app/src/pages/contribute-data/_components/ValidationChecks.tsx
similarity index 97%
rename from strudel-demo-app/src/app/contribute-data/ValidationChecks.tsx
rename to strudel-demo-app/src/pages/contribute-data/_components/ValidationChecks.tsx
index 8c0353bd..b8f5a332 100644
--- a/strudel-demo-app/src/app/contribute-data/ValidationChecks.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/_components/ValidationChecks.tsx
@@ -1,7 +1,7 @@
 import { Box, LinearProgress, Paper, Stack, Typography } from '@mui/material';
 import React from 'react';
-import { LinearMeter } from '../../components/LinearMeter';
-import { useContributeData } from './context/ContextProvider';
+import { LinearMeter } from '../../../components/LinearMeter';
+import { useContributeData } from '../_context/ContextProvider';
   
 /**
  * Panel to display information and status of validation checks in the contribute-data Task Flow.
diff --git a/strudel-demo-app/src/app/contribute-data/definitions.json b/strudel-demo-app/src/pages/contribute-data/_config/definitions.json
similarity index 100%
rename from strudel-demo-app/src/app/contribute-data/definitions.json
rename to strudel-demo-app/src/pages/contribute-data/_config/definitions.json
diff --git a/strudel-demo-app/src/app/contribute-data/context/ContextProvider.tsx b/strudel-demo-app/src/pages/contribute-data/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/app/contribute-data/context/ContextProvider.tsx
rename to strudel-demo-app/src/pages/contribute-data/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/app/contribute-data/context/actions.ts b/strudel-demo-app/src/pages/contribute-data/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/app/contribute-data/context/actions.ts
rename to strudel-demo-app/src/pages/contribute-data/_context/actions.ts
diff --git a/strudel-demo-app/src/app/contribute-data/context/utils.ts b/strudel-demo-app/src/pages/contribute-data/_context/utils.ts
similarity index 100%
rename from strudel-demo-app/src/app/contribute-data/context/utils.ts
rename to strudel-demo-app/src/pages/contribute-data/_context/utils.ts
diff --git a/strudel-demo-app/src/app/contribute-data/ContributeDataWrapper.tsx b/strudel-demo-app/src/pages/contribute-data/_layout.tsx
similarity index 76%
rename from strudel-demo-app/src/app/contribute-data/ContributeDataWrapper.tsx
rename to strudel-demo-app/src/pages/contribute-data/_layout.tsx
index 981f5af9..714f7723 100644
--- a/strudel-demo-app/src/app/contribute-data/ContributeDataWrapper.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/_layout.tsx
@@ -2,13 +2,13 @@ import { Box } from '@mui/material';
 import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
-import { ContributeDataProvider } from './context/ContextProvider';
+import { ContributeDataProvider } from './_context/ContextProvider';
 
 /**
  * Top-level wrapper for the contribute-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const ContributeDataWrapper: React.FC = () => {
+const ContributeDataLayout: React.FC = () => {
   return (
     <Box>
       <Box sx={{ flexGrow: 1 }}>
@@ -21,4 +21,6 @@ export const ContributeDataWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default ContributeDataLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/contribute-data/Register.tsx b/strudel-demo-app/src/pages/contribute-data/index.tsx
similarity index 98%
rename from strudel-demo-app/src/app/contribute-data/Register.tsx
rename to strudel-demo-app/src/pages/contribute-data/index.tsx
index 34c07294..d40dd382 100644
--- a/strudel-demo-app/src/app/contribute-data/Register.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/index.tsx
@@ -7,7 +7,7 @@ import { LabelValueTable } from '../../components/LabelValueTable';
  * Initial page for registering as a contributor in the contribute-data Task Flow.
  * After submitting, users are directed to the `<ContributorPortal>`.
  */
-export const Register: React.FC = () => {
+const Register: React.FC = () => {
   return (
     <Container
         maxWidth="md"
@@ -87,4 +87,6 @@ export const Register: React.FC = () => {
         </Paper>
       </Container>
   )
-}
\ No newline at end of file
+}
+
+export default Register;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/contribute-data/NewDataset.tsx b/strudel-demo-app/src/pages/contribute-data/new.tsx
similarity index 90%
rename from strudel-demo-app/src/app/contribute-data/NewDataset.tsx
rename to strudel-demo-app/src/pages/contribute-data/new.tsx
index 9877037c..eaf15a1b 100644
--- a/strudel-demo-app/src/app/contribute-data/NewDataset.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/new.tsx
@@ -1,14 +1,14 @@
 import { Box, Button, Container, Grid, Link, Stack, Typography } from '@mui/material';
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { DataFilesPanel } from './DataFilesPanel';
-import { MetadataPanel } from './MetadataPanel';
+import { DataFilesPanel } from './_components/DataFilesPanel';
+import { MetadataPanel } from './_components/MetadataPanel';
 
 /**
  * Page for entering information about a new dataset in the contribute-data Task Flow.
  * Includes the `<MetadataPanel>` and `<DataFilesPanel>` for adding input data.
  */
-export const NewDataset: React.FC = () => {  
+const NewDataset: React.FC = () => {  
   return (
     <Container
       maxWidth="xl"
@@ -59,4 +59,6 @@ export const NewDataset: React.FC = () => {
       </Stack>
     </Container>
   );
-}
\ No newline at end of file
+}
+
+export default NewDataset;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/contribute-data/ContributorPortal.tsx b/strudel-demo-app/src/pages/contribute-data/portal.tsx
similarity index 92%
rename from strudel-demo-app/src/app/contribute-data/ContributorPortal.tsx
rename to strudel-demo-app/src/pages/contribute-data/portal.tsx
index 7d41546f..06249895 100644
--- a/strudel-demo-app/src/app/contribute-data/ContributorPortal.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/portal.tsx
@@ -6,14 +6,14 @@ import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import definitions from './definitions.json';
+import definitions from './_config/definitions.json';
+import { basename } from '../../main';
 
 /**
  * Page to show a contributor's uploads in the contribute-data Task Flow.
  * Also allows users to start a new dataset which sends them to the `<NewDataset>` component.
  */
-export const ContributorPortal: React.FC = () => {
+const ContributorPortal: React.FC = () => {
   // strudel-kit-variable-next-line
   const datasets = useDataFromSource('default/contribute-data/contributor_datasets.json', basename);
   
@@ -69,4 +69,6 @@ const columns: GridColDef[] = [
     ],
     flex: 1,
   },
-];
\ No newline at end of file
+];
+
+export default ContributorPortal;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/contribute-data/ReviewDataset.tsx b/strudel-demo-app/src/pages/contribute-data/review.tsx
similarity index 90%
rename from strudel-demo-app/src/app/contribute-data/ReviewDataset.tsx
rename to strudel-demo-app/src/pages/contribute-data/review.tsx
index 9494e3f9..764d4a86 100644
--- a/strudel-demo-app/src/app/contribute-data/ReviewDataset.tsx
+++ b/strudel-demo-app/src/pages/contribute-data/review.tsx
@@ -2,17 +2,17 @@ import CloseIcon from '@mui/icons-material/Close';
 import { Alert, Box, Button, Collapse, Container, Grid, IconButton, Link, Stack, Typography } from '@mui/material';
 import React, { useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { DatasetView } from './DatasetView';
-import { ValidationChecks } from './ValidationChecks';
-import { useContributeData } from './context/ContextProvider';
-import { finishChecks, runChecks } from './context/actions';
+import { DatasetView } from './_components/DatasetView';
+import { ValidationChecks } from './_components/ValidationChecks';
+import { useContributeData } from './_context/ContextProvider';
+import { finishChecks, runChecks } from './_context/actions';
 
 /**
  * Page to review a new dataset before submitting it in the contribute-data Task Flow.
  * Users can see read-only data in the `<DatasetView>`, go back to editing, or run checks 
  * against their uploaded dataset and see validation in the `<ValidationChecks>` component.
  */
-export const ReviewDataset: React.FC = () => {
+const ReviewDataset: React.FC = () => {
   const [alertOpen, setAlertOpen] = useState(true);
   const { state, dispatch } = useContributeData();
 
@@ -111,4 +111,6 @@ export const ReviewDataset: React.FC = () => {
       </Container>
     </Box>
   );
-}
\ No newline at end of file
+}
+
+export default ReviewDataset;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/explore-data/DataDetailPage.tsx b/strudel-demo-app/src/pages/explore-data/[id].tsx
similarity index 86%
rename from strudel-demo-app/src/app/explore-data/DataDetailPage.tsx
rename to strudel-demo-app/src/pages/explore-data/[id].tsx
index b240716b..a5a136e8 100644
--- a/strudel-demo-app/src/app/explore-data/DataDetailPage.tsx
+++ b/strudel-demo-app/src/pages/explore-data/[id].tsx
@@ -2,19 +2,19 @@ import { Box, Container, Paper, Stack, Typography } from '@mui/material';
 import React from 'react';
 import { useParams } from 'react-router-dom';
 import { PageHeader } from '../../components/PageHeader';
-import { useExploreData } from './context/ContextProvider';
+import { useExploreData } from './_context/ContextProvider';
 
 /**
  * Work in Progress:
  * 
  * Detail view for a selected row from the` <DataExplorer>` in the explore-data Task Flow.
  */
-export const DataDetailPage: React.FC = () => {
+const DataDetailPage: React.FC = () => {
   const {state, dispatch} = useExploreData();
   const params = useParams();
   const entity = state.data?.find((d) => {
-    if (params.datasetId) {
-      return d[state.dataIdField].toString() === params.datasetId.toString();
+    if (params.id) {
+      return d[state.dataIdField].toString() === params.id.toString();
     }
   });
   console.log(state);
@@ -61,4 +61,6 @@ export const DataDetailPage: React.FC = () => {
       </Container>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default DataDetailPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/explore-data/DataTablePanel.tsx b/strudel-demo-app/src/pages/explore-data/_components/DataTablePanel.tsx
similarity index 94%
rename from strudel-demo-app/src/app/explore-data/DataTablePanel.tsx
rename to strudel-demo-app/src/pages/explore-data/_components/DataTablePanel.tsx
index a24ca2af..8453cb59 100644
--- a/strudel-demo-app/src/app/explore-data/DataTablePanel.tsx
+++ b/strudel-demo-app/src/pages/explore-data/_components/DataTablePanel.tsx
@@ -3,8 +3,8 @@ import { Button, Paper, Stack, TextField, Typography } from '@mui/material';
 import { GridEventListener } from '@mui/x-data-grid';
 import React from 'react';
 import { DataGrid } from '@mui/x-data-grid';
-import { useExploreData } from './context/ContextProvider';
-import { setPreviewItem, setSearch } from './context/actions';
+import { useExploreData } from '../_context/ContextProvider';
+import { setPreviewItem, setSearch } from '../_context/actions';
 
 interface DataTablePanelProps {
   onToggleFiltersPanel: () => any
diff --git a/strudel-demo-app/src/app/explore-data/FiltersPanel.tsx b/strudel-demo-app/src/pages/explore-data/_components/FiltersPanel.tsx
similarity index 87%
rename from strudel-demo-app/src/app/explore-data/FiltersPanel.tsx
rename to strudel-demo-app/src/pages/explore-data/_components/FiltersPanel.tsx
index c451eca9..c46fa398 100644
--- a/strudel-demo-app/src/app/explore-data/FiltersPanel.tsx
+++ b/strudel-demo-app/src/pages/explore-data/_components/FiltersPanel.tsx
@@ -1,11 +1,11 @@
 import React from 'react';
-import { Filters } from '../../components/Filters';
-import { FilterField } from '../../components/FilterField';
-import { CheckboxList, CheckboxOption } from '../../components/CheckboxList';
-import { useExploreData } from './context/ContextProvider';
-import { StrudelSlider } from '../../components/StrudelSlider';
-import { setFilter } from './context/actions';
-import { FilterConfig, FilterOperator } from '../../types/filters.types';
+import { Filters } from '../../../components/Filters';
+import { FilterField } from '../../../components/FilterField';
+import { CheckboxList, CheckboxOption } from '../../../components/CheckboxList';
+import { useExploreData } from '../_context/ContextProvider';
+import { StrudelSlider } from '../../../components/StrudelSlider';
+import { setFilter } from '../_context/actions';
+import { FilterConfig, FilterOperator } from '../../../types/filters.types';
 import { Stack } from '@mui/material';
 import { DatePicker } from '@mui/x-date-pickers';
 
diff --git a/strudel-demo-app/src/app/explore-data/PreviewPanel.tsx b/strudel-demo-app/src/pages/explore-data/_components/PreviewPanel.tsx
similarity index 96%
rename from strudel-demo-app/src/app/explore-data/PreviewPanel.tsx
rename to strudel-demo-app/src/pages/explore-data/_components/PreviewPanel.tsx
index 45fc1093..ae564641 100644
--- a/strudel-demo-app/src/app/explore-data/PreviewPanel.tsx
+++ b/strudel-demo-app/src/pages/explore-data/_components/PreviewPanel.tsx
@@ -2,9 +2,9 @@ import React from 'react';
 import { Box, Button, IconButton, Link, Paper, Stack, Typography } from '@mui/material';
 import CloseIcon from '@mui/icons-material/Close';
 import { Link as RouterLink } from 'react-router-dom';
-import { LabelValueTable } from '../../components/LabelValueTable';
+import { LabelValueTable } from '../../../components/LabelValueTable';
 import { DataGrid } from '@mui/x-data-grid';
-import { useExploreData } from './context/ContextProvider';
+import { useExploreData } from '../_context/ContextProvider';
 
 interface PreviewPanelProps {
   onClose: () => any
diff --git a/strudel-demo-app/src/app/explore-data/definitions.json b/strudel-demo-app/src/pages/explore-data/_config/definitions.json
similarity index 100%
rename from strudel-demo-app/src/app/explore-data/definitions.json
rename to strudel-demo-app/src/pages/explore-data/_config/definitions.json
diff --git a/strudel-demo-app/src/app/explore-data/context/ContextProvider.tsx b/strudel-demo-app/src/pages/explore-data/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/app/explore-data/context/ContextProvider.tsx
rename to strudel-demo-app/src/pages/explore-data/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/app/explore-data/context/actions.ts b/strudel-demo-app/src/pages/explore-data/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/app/explore-data/context/actions.ts
rename to strudel-demo-app/src/pages/explore-data/_context/actions.ts
diff --git a/strudel-demo-app/src/app/explore-data/ExploreDataWrapper.tsx b/strudel-demo-app/src/pages/explore-data/_layout.tsx
similarity index 80%
rename from strudel-demo-app/src/app/explore-data/ExploreDataWrapper.tsx
rename to strudel-demo-app/src/pages/explore-data/_layout.tsx
index 79aa4986..ca8117e1 100644
--- a/strudel-demo-app/src/app/explore-data/ExploreDataWrapper.tsx
+++ b/strudel-demo-app/src/pages/explore-data/_layout.tsx
@@ -1,17 +1,17 @@
 import React from 'react';
-import { basename } from '../App';
 import { Box } from '@mui/material';
 import { Outlet } from 'react-router';
-import { ExploreDataProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { ExploreDataProvider } from './_context/ContextProvider';
+import definitions from './_config/definitions.json';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
+import { basename } from '../../main';
 
 /**
  * Top-level wrapper for the explore-data Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const ExploreDataWrapper: React.FC = () => {
+const ExploreDataLayout: React.FC = () => {
   // strudel-kit-variable-next-line
   const entities = useDataFromSource('default/explore-data/genomes.tsv', basename);
 
@@ -36,4 +36,6 @@ export const ExploreDataWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default ExploreDataLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/explore-data/DataExplorer.tsx b/strudel-demo-app/src/pages/explore-data/index.tsx
similarity index 84%
rename from strudel-demo-app/src/app/explore-data/DataExplorer.tsx
rename to strudel-demo-app/src/pages/explore-data/index.tsx
index 8f24de8f..ae34484c 100644
--- a/strudel-demo-app/src/app/explore-data/DataExplorer.tsx
+++ b/strudel-demo-app/src/pages/explore-data/index.tsx
@@ -1,18 +1,18 @@
 import { Box, Grid } from '@mui/material';
 import React, { useState } from 'react';
-import { FiltersPanel } from './FiltersPanel';
-import { PreviewPanel } from './PreviewPanel';
-import { DataTablePanel } from './DataTablePanel';
+import { FiltersPanel } from './_components/FiltersPanel';
+import { PreviewPanel } from './_components/PreviewPanel';
+import { DataTablePanel } from './_components/DataTablePanel';
 import { PageHeader } from '../../components/PageHeader';
-import { useExploreData } from './context/ContextProvider';
-import { setPreviewItem } from './context/actions';
+import { useExploreData } from './_context/ContextProvider';
+import { setPreviewItem } from './_context/actions';
 
 /**
  * Main explorer page in the explore-data Task Flow.
  * This page includes the page header, filters panel, 
  * main table, and the table row preview panel.
  */
-export const DataExplorer: React.FC = () => {
+const DataExplorer: React.FC = () => {
   const {state, dispatch} = useExploreData();
   const [showFiltersPanel, setShowFiltersPanel] = useState(true);
 
@@ -72,4 +72,6 @@ const getMainColumnSize = (showFiltersPanel: boolean, showPreviewPanel: boolean)
   } else if (showFiltersPanel && showPreviewPanel) {
     return 6;
   }
-}
\ No newline at end of file
+}
+
+export default DataExplorer;
\ No newline at end of file
diff --git a/strudel-demo-app/src/pages/index.tsx b/strudel-demo-app/src/pages/index.tsx
new file mode 100644
index 00000000..74142192
--- /dev/null
+++ b/strudel-demo-app/src/pages/index.tsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import { Box, Container, Paper, Stack, Typography } from '@mui/material';
+import { TopBar } from '../components/TopBar';
+import { routes } from '@generouted/react-router';
+import { AppLink } from '../components/AppLink';
+
+/**
+ * TODO:
+ * - Add instructions about how to rig up the router for task flows
+ * - Add instructions for putting dataset into app
+ */
+const HomePage: React.FC = () => {
+  return (
+    <Box>
+      <TopBar />
+      <Container
+        maxWidth="md"
+        sx={{
+          marginTop: 3,
+          marginBottom: 3,
+        }}
+      >
+        <Stack>
+          <Paper
+            sx={{
+              padding: 2
+            }}
+          >
+            <Typography variant="h5" component="h1" fontWeight="bold">Welcome to your app built with STRUDEL!</Typography>
+          </Paper>
+          <Paper
+            sx={{
+              padding: 2
+            }}
+          >
+            <Stack>
+              <Typography variant="h6">Registered Pages</Typography>
+              {routes[0].children?.map((route) => {
+                if (route.path !== '/' && route.path !== '*') {
+                  return (
+                    <Box key={route.path}>
+                      <AppLink to={route.path || ''}>{route.path}</AppLink>
+                    </Box>
+                  )
+                }
+              })}
+            </Stack>
+          </Paper>
+        </Stack>
+      </Container>
+    </Box>
+  )
+}
+
+export default HomePage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/monitor-activities/MonitorActivitiesWrapper.tsx b/strudel-demo-app/src/pages/monitor-activities/_layout.tsx
similarity index 82%
rename from strudel-demo-app/src/app/monitor-activities/MonitorActivitiesWrapper.tsx
rename to strudel-demo-app/src/pages/monitor-activities/_layout.tsx
index 1c951078..b99a71f5 100644
--- a/strudel-demo-app/src/app/monitor-activities/MonitorActivitiesWrapper.tsx
+++ b/strudel-demo-app/src/pages/monitor-activities/_layout.tsx
@@ -7,7 +7,7 @@ import { TopBar } from '../../components/TopBar';
  * Top-level wrapper for the monitor-activities Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const MonitorActivitiesWrapper: React.FC = () => {
+const MonitorActivitiesLayout: React.FC = () => {
   return (
     <Box>
       <Box sx={{ flexGrow: 1 }}>
@@ -18,4 +18,6 @@ export const MonitorActivitiesWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default MonitorActivitiesLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/monitor-activities/ActivityCalendar.tsx b/strudel-demo-app/src/pages/monitor-activities/calendar.tsx
similarity index 87%
rename from strudel-demo-app/src/app/monitor-activities/ActivityCalendar.tsx
rename to strudel-demo-app/src/pages/monitor-activities/calendar.tsx
index 883c37f2..3bfdeb24 100644
--- a/strudel-demo-app/src/app/monitor-activities/ActivityCalendar.tsx
+++ b/strudel-demo-app/src/pages/monitor-activities/calendar.tsx
@@ -9,7 +9,7 @@ import { Link as RouterLink } from 'react-router-dom';
  * 
  * Page to see all activities by day in a calendar view in the monitor-activities Task Flow.
  */
-export const ActivityCalendar: React.FC = () => {
+const ActivityCalendar: React.FC = () => {
   return (
     <Container
       maxWidth="md"
@@ -25,9 +25,11 @@ export const ActivityCalendar: React.FC = () => {
       <LocalizationProvider dateAdapter={AdapterDayjs}>
         <DateCalendar readOnly />
       </LocalizationProvider>
-      <Link component={RouterLink} to="list">
+      <Link component={RouterLink} to="../">
         List
       </Link>
     </Container>
   )
-}
\ No newline at end of file
+}
+
+export default ActivityCalendar;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/monitor-activities/ActivityDetail.tsx b/strudel-demo-app/src/pages/monitor-activities/detail.tsx
similarity index 96%
rename from strudel-demo-app/src/app/monitor-activities/ActivityDetail.tsx
rename to strudel-demo-app/src/pages/monitor-activities/detail.tsx
index 79ce43d8..e2098760 100644
--- a/strudel-demo-app/src/app/monitor-activities/ActivityDetail.tsx
+++ b/strudel-demo-app/src/pages/monitor-activities/detail.tsx
@@ -8,13 +8,13 @@ import Plot from 'react-plotly.js';
 import { Link as RouterLink } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
+import { basename } from '../../main';
 
 /**
  * Detail view of the selected activity from `<ActivityList>` in monitor-activities Task Flow.
  * The two components are not currently hooked together.
  */
-export const ActivityDetail: React.FC = () => {
+const ActivityDetail: React.FC = () => {
   // strudel-kit-variable-next-line
   const experiment = useDataFromSource('default/monitor-activities/experiment_detail.json', basename);
 
@@ -39,7 +39,7 @@ export const ActivityDetail: React.FC = () => {
     >
       <Stack>
         <Stack direction="row" alignItems="center">
-          <Link component={RouterLink} to="/monitor-activities/list">
+          <Link component={RouterLink} to="../">
             <IconButton>
               <ArrowBackIcon />
             </IconButton>
@@ -157,4 +157,6 @@ const columns: GridColDef[] = [
     type: 'number',
     width: 200 
   }
-];
\ No newline at end of file
+];
+
+export default ActivityDetail;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/monitor-activities/ActivityList.tsx b/strudel-demo-app/src/pages/monitor-activities/index.tsx
similarity index 90%
rename from strudel-demo-app/src/app/monitor-activities/ActivityList.tsx
rename to strudel-demo-app/src/pages/monitor-activities/index.tsx
index 12a0f13c..6ed6ed29 100644
--- a/strudel-demo-app/src/app/monitor-activities/ActivityList.tsx
+++ b/strudel-demo-app/src/pages/monitor-activities/index.tsx
@@ -4,12 +4,12 @@ import dayjs from 'dayjs';
 import React from 'react';
 import { useNavigate } from 'react-router-dom';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
+import { basename } from '../../main';
 
 /**
  * List view of all activities in the monitor-activites Task Flow.
  */
-export const ActivityList: React.FC = () => {
+const ActivityList: React.FC = () => {
   // strudel-kit-variable-next-line
   const experiments = useDataFromSource('default/monitor-activities/experiments.json', basename);
   const navigate = useNavigate();
@@ -28,7 +28,7 @@ export const ActivityList: React.FC = () => {
       <Stack>
         <Stack direction="row" justifyContent="space-between" alignItems="center">
           <Typography variant="h6" component="h1">
-            Experiments
+            Experiments test
           </Typography>
         </Stack>
         <Paper>
@@ -41,7 +41,7 @@ export const ActivityList: React.FC = () => {
                 sortModel: [{ field: 'start_time', sort: 'desc' }],
               },
             }}
-            onRowClick={() => navigate('list/detail')}
+            onRowClick={() => navigate('detail')}
             disableColumnSelector
             disableRowSelectionOnClick
           />
@@ -70,7 +70,6 @@ const columns: GridColDef[] = [
   { 
     field: 'end_time', 
     headerName: 'End Time', 
-    type: 'datetime',
     width: 200 
   },
   { 
@@ -78,4 +77,6 @@ const columns: GridColDef[] = [
     headerName: 'Status', 
     width: 200 
   }
-];
\ No newline at end of file
+];
+
+export default ActivityList;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/Scenario.tsx b/strudel-demo-app/src/pages/run-computation/[id]/_layout.tsx
similarity index 94%
rename from strudel-demo-app/src/app/run-computation/Scenario.tsx
rename to strudel-demo-app/src/pages/run-computation/[id]/_layout.tsx
index 20b02811..4bfd2be5 100644
--- a/strudel-demo-app/src/app/run-computation/Scenario.tsx
+++ b/strudel-demo-app/src/pages/run-computation/[id]/_layout.tsx
@@ -8,7 +8,7 @@ import { Outlet, Link as RouterLink } from 'react-router-dom';
  * This goes from `<DataInputs>` to `<Settings>` to `<RunningComputation>` to `<Results>`.
  * These inner steps are rendered by the `<Outlet>` component.
  */
-export const Scenario: React.FC = () => {
+const ComputationLayout: React.FC = () => {
   return (
     <Stack spacing={0} height="100vh">
       <Box>
@@ -37,4 +37,6 @@ export const Scenario: React.FC = () => {
       <Outlet />
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default ComputationLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/DataInputs.tsx b/strudel-demo-app/src/pages/run-computation/[id]/data-inputs.tsx
similarity index 93%
rename from strudel-demo-app/src/app/run-computation/DataInputs.tsx
rename to strudel-demo-app/src/pages/run-computation/[id]/data-inputs.tsx
index 05387bb2..6609d9da 100644
--- a/strudel-demo-app/src/app/run-computation/DataInputs.tsx
+++ b/strudel-demo-app/src/pages/run-computation/[id]/data-inputs.tsx
@@ -2,17 +2,17 @@ import { Box, Button, Container, Link, Paper, Stack, Step, StepLabel, Stepper, T
 import React, { useEffect } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
 import { DataGrid } from '@mui/x-data-grid';
-import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { useRunComputation } from './context/ContextProvider';
-import { setInputsTableData } from './context/actions';
+import { useDataFromSource } from '../../../utils/useDataFromSource';
+import { basename } from '../../../main';
+import { useRunComputation } from '../_context/ContextProvider';
+import { setInputsTableData } from '../_context/actions';
 
 /**
  * Page to display input data after creating or selecting an item from 
  * the `<ComputationsList>` page in the run-computation Task Flow.
  * Table columns are configured in `definitions.inputs.table.columns`
  */
-export const DataInputs: React.FC = () => {
+const DataInputsPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
   // strudel-kit-variable-next-line
   const inputsData = useDataFromSource('default/run-computation/inputs.json', basename);
@@ -160,4 +160,6 @@ export const DataInputs: React.FC = () => {
       </Box>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default DataInputsPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/Results.tsx b/strudel-demo-app/src/pages/run-computation/[id]/results.tsx
similarity index 95%
rename from strudel-demo-app/src/app/run-computation/Results.tsx
rename to strudel-demo-app/src/pages/run-computation/[id]/results.tsx
index addeb320..2d6f2c19 100644
--- a/strudel-demo-app/src/app/run-computation/Results.tsx
+++ b/strudel-demo-app/src/pages/run-computation/[id]/results.tsx
@@ -3,16 +3,16 @@ import { DataGrid } from '@mui/x-data-grid';
 import React, { useEffect } from 'react';
 import Plot from 'react-plotly.js';
 import { Link as RouterLink } from 'react-router-dom';
-import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { useRunComputation } from './context/ContextProvider';
-import { setResultsBarChartData, setResultsLineChartData, setResultsTableData } from './context/actions';
+import { useDataFromSource } from '../../../utils/useDataFromSource';
+import { basename } from '../../../main';
+import { useRunComputation } from '../_context/ContextProvider';
+import { setResultsBarChartData, setResultsLineChartData, setResultsTableData } from '../_context/actions';
 
 /**
  * Results page to display after a computation completes in the run-computation Task Flow.
  * Displays a line chart, bar chart, and table of results from the computation.
  */
-export const Results: React.FC = () => {
+const ResultsPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
   // strudel-kit-variable-next-line
   const tableData = useDataFromSource('default/run-computation/results_table.json', basename);
@@ -201,4 +201,6 @@ export const Results: React.FC = () => {
       </Box>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default ResultsPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/RunningComputation.tsx b/strudel-demo-app/src/pages/run-computation/[id]/running.tsx
similarity index 89%
rename from strudel-demo-app/src/app/run-computation/RunningComputation.tsx
rename to strudel-demo-app/src/pages/run-computation/[id]/running.tsx
index 90672962..2a6a4be2 100644
--- a/strudel-demo-app/src/app/run-computation/RunningComputation.tsx
+++ b/strudel-demo-app/src/pages/run-computation/[id]/running.tsx
@@ -1,26 +1,23 @@
 import { Box, Button, Container, LinearProgress, Link, Paper, Stack, Step, StepLabel, Stepper, Typography } from '@mui/material';
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { useInterval } from '../../utils/useInterval';
+import { useInterval } from '../../../utils/useInterval';
 
 /**
  * Page to show while a computation is running and after it completes.
  * Continuing after completion, this page takes users to the `<Results>` page.
  */
-export const RunningComputation: React.FC = () => {
+const RunningComputationPage: React.FC = () => {
   const [running, setRunning] = useState(true);
-  const [progress, setProgress] = useState(0);
 
   /**
    * Simulate the time it takes to run the optimization
    */
-  useInterval(() => {
-    if (progress < 100) {
-      setProgress(progress + 10);
-    } else {
+  useEffect(() => {
+    setTimeout(() => {
       setRunning(false);
-    }
-  }, 500);
+    }, 2000)
+  })
 
   /**
    * Content to render on the page for this component
@@ -78,7 +75,7 @@ export const RunningComputation: React.FC = () => {
                 <Typography>This could take several minutes.</Typography>
                 <Typography>You may leave this page and return later. Your progress will not be affected.</Typography>
               </Box>
-              <LinearProgress variant="determinate" value={progress} sx={{ height: 10 }} />
+              <LinearProgress variant="indeterminate" sx={{ height: 10 }} />
               <Typography color="neutral.dark">
                 Started 05/24/2023 12:32:33
               </Typography>
@@ -104,4 +101,6 @@ export const RunningComputation: React.FC = () => {
       </Container>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default RunningComputationPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/Settings.tsx b/strudel-demo-app/src/pages/run-computation/[id]/settings.tsx
similarity index 98%
rename from strudel-demo-app/src/app/run-computation/Settings.tsx
rename to strudel-demo-app/src/pages/run-computation/[id]/settings.tsx
index 71b2b5dc..c7328533 100644
--- a/strudel-demo-app/src/app/run-computation/Settings.tsx
+++ b/strudel-demo-app/src/pages/run-computation/[id]/settings.tsx
@@ -7,7 +7,7 @@ import { Link as RouterLink } from 'react-router-dom';
  * Completing and submitting the form takes users to the 
  * `<RunningComputation>` component.
  */
-export const Settings: React.FC = () => {
+const SettingsPage: React.FC = () => {
   const [showAdvanced, setShowAdvanced] = useState(false);
 
   const handleAdvancedToggle = () => {
@@ -141,4 +141,6 @@ export const Settings: React.FC = () => {
       </Container>
     </Stack>
   )
-}
\ No newline at end of file
+}
+
+export default SettingsPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/NewScenarioModal.tsx b/strudel-demo-app/src/pages/run-computation/_components/NewScenarioModal.tsx
similarity index 100%
rename from strudel-demo-app/src/app/run-computation/NewScenarioModal.tsx
rename to strudel-demo-app/src/pages/run-computation/_components/NewScenarioModal.tsx
diff --git a/strudel-demo-app/src/app/run-computation/definitions.json b/strudel-demo-app/src/pages/run-computation/_config/definitions.json
similarity index 100%
rename from strudel-demo-app/src/app/run-computation/definitions.json
rename to strudel-demo-app/src/pages/run-computation/_config/definitions.json
diff --git a/strudel-demo-app/src/app/run-computation/context/ContextProvider.tsx b/strudel-demo-app/src/pages/run-computation/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/app/run-computation/context/ContextProvider.tsx
rename to strudel-demo-app/src/pages/run-computation/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/app/run-computation/context/actions.ts b/strudel-demo-app/src/pages/run-computation/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/app/run-computation/context/actions.ts
rename to strudel-demo-app/src/pages/run-computation/_context/actions.ts
diff --git a/strudel-demo-app/src/app/run-computation/RunComputationWrapper.tsx b/strudel-demo-app/src/pages/run-computation/_layout.tsx
similarity index 90%
rename from strudel-demo-app/src/app/run-computation/RunComputationWrapper.tsx
rename to strudel-demo-app/src/pages/run-computation/_layout.tsx
index 927cc49e..122e476c 100644
--- a/strudel-demo-app/src/app/run-computation/RunComputationWrapper.tsx
+++ b/strudel-demo-app/src/pages/run-computation/_layout.tsx
@@ -7,15 +7,15 @@ import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { RunComputationProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { basename } from '../../main';
+import { RunComputationProvider } from './_context/ContextProvider';
+import definitions from './_config/definitions.json';
 
 /**
  * Top-level wrapper for the run-computation Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const RunComputationWrapper: React.FC = () => {
+const RunComputationLayout: React.FC = () => {
   // strudel-kit-variable-next-line
   const listItems = useDataFromSource('default/run-computation/list.json', basename)
 
@@ -72,4 +72,6 @@ export const RunComputationWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default RunComputationLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/run-computation/ComputationsList.tsx b/strudel-demo-app/src/pages/run-computation/index.tsx
similarity index 91%
rename from strudel-demo-app/src/app/run-computation/ComputationsList.tsx
rename to strudel-demo-app/src/pages/run-computation/index.tsx
index 6894aaaa..11d8a3d2 100644
--- a/strudel-demo-app/src/app/run-computation/ComputationsList.tsx
+++ b/strudel-demo-app/src/pages/run-computation/index.tsx
@@ -1,15 +1,15 @@
 import { Box, Button, Container, Paper, Stack, Typography } from '@mui/material';
 import React, { useState } from 'react';
 import { DataGrid } from '@mui/x-data-grid';
-import { NewScenarioModal } from './NewScenarioModal';
-import { useRunComputation } from './context/ContextProvider';
+import { NewScenarioModal } from './_components/NewScenarioModal';
+import { useRunComputation } from './_context/ContextProvider';
 
 /**
  * List page for all compuation runs in the run-computation Task Flow.
  * Can open the `<NewScenarioModal>` from here and then proceed to the 
  * `<DataInputs>` component for the next step.
  */
-export const ComputationsList: React.FC = () => {
+export const ListPage: React.FC = () => {
   const { state, dispatch } = useRunComputation();
   const [modalOpen, setModalOpen] = useState(false);
 
@@ -73,4 +73,6 @@ export const ComputationsList: React.FC = () => {
       </Container>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default ListPage;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/search-data-repositories/DatasetDetail.tsx b/strudel-demo-app/src/pages/search-data-repositories/[id].tsx
similarity index 95%
rename from strudel-demo-app/src/app/search-data-repositories/DatasetDetail.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/[id].tsx
index 65d180f2..55e685e6 100644
--- a/strudel-demo-app/src/app/search-data-repositories/DatasetDetail.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/[id].tsx
@@ -2,23 +2,23 @@ import DownloadIcon from '@mui/icons-material/Download';
 import VisibilityIcon from '@mui/icons-material/Visibility';
 import { TabContext, TabList, TabPanel } from '@mui/lab';
 import { Box, Button, Container, Grid, Link, Paper, Stack, Tab, Typography } from '@mui/material';
-import { DataGrid, GridActionsCellItem, GridRowParams } from '@mui/x-data-grid';
+import { DataGrid, GridActionsCellItem, GridColDef, GridRowParams } from '@mui/x-data-grid';
 import React from 'react';
 import { useParams } from 'react-router-dom';
 import { LabelValueTable } from '../../components/LabelValueTable';
 import { PageHeader } from '../../components/PageHeader';
-import { useSearchDataRepositories } from './context/ContextProvider';
+import { useSearchDataRepositories } from './_context/ContextProvider';
 
 /**
  * Detail page for a particular dataset. This is accessed by clicking on a 
  * card title or the link in the `<PreviewPanel>`.
  */
-export const DatasetDetail: React.FC = () => {
+const DatasetDetail: React.FC = () => {
   const {state, dispatch} = useSearchDataRepositories();
   const params = useParams();
   const dataset = state.data?.find((d) => {
-    if (params.datasetId) {
-      return d.id === parseInt(params.datasetId);
+    if (params.id) {
+      return d.id === parseInt(params.id);
     }
   });
   const datasetTitle = dataset ? dataset['title'] : 'Not Found';
@@ -184,7 +184,7 @@ export const DatasetDetail: React.FC = () => {
 /**
  * Attached files data configured in-file for prototyping
  */
-const attachedFilesColumns = [
+const attachedFilesColumns: GridColDef[] = [
   { 
     field: 'file_name', 
     headerName: 'File Name',
@@ -210,4 +210,6 @@ const attachedFilesColumns = [
     ],
     flex: 1
   },
-];
\ No newline at end of file
+];
+
+export default DatasetDetail;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/search-data-repositories/DataListCard.tsx b/strudel-demo-app/src/pages/search-data-repositories/_components/DataListCard.tsx
similarity index 79%
rename from strudel-demo-app/src/app/search-data-repositories/DataListCard.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/_components/DataListCard.tsx
index c0fef5e4..17a8c20a 100644
--- a/strudel-demo-app/src/app/search-data-repositories/DataListCard.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/_components/DataListCard.tsx
@@ -2,8 +2,8 @@ import { Box, Link, Stack, Typography } from '@mui/material';
 import { blue } from '@mui/material/colors';
 import React from 'react';
 import { Link as RouterLink } from 'react-router-dom';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setPreviewItem } from './context/actions';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
+import { setPreviewItem } from '../_context/actions';
 
 interface DataListCardProps {
   item: any;
@@ -61,20 +61,20 @@ export const DataListCard: React.FC<DataListCardProps> = ({ item }) => {
         {state.cardFields.content && (
           <Typography
             sx={{
-              '-webkit-box-orient': 'vertical',
-              '-webkit-line-clamp': '2',
+              WebkitBoxOrient: 'vertical',
+              WebkitLineClamp: '2',
               display: '-webkit-box',
               overflow: 'hidden'
             }}
           >
-            {item[state.cardFields.content]}
+            {item[state.cardFields.content]} test
           </Typography>
         )}
         {state.cardFields.tags && (
           <Typography
             sx={{
-              '-webkit-box-orient': 'vertical',
-              '-webkit-line-clamp': '1',
+              WebkitBoxOrient: 'vertical',
+              WebkitLineClamp: '1',
               display: '-webkit-box',
               fontStyle: 'italic',
               overflow: 'hidden'
@@ -82,9 +82,9 @@ export const DataListCard: React.FC<DataListCardProps> = ({ item }) => {
           >
             {item[state.cardFields.tags].map((tag: string, i: number) => {
               if (i < item['tags'].length -1) {
-                return <Typography component="span" sx={{ fontSize: 'small', marginRight: 0.5 }}>{tag},</Typography>
+                return <Typography key={`${tag}-${i}`} component="span" sx={{ fontSize: 'small', marginRight: 0.5 }}>{tag},</Typography>
               } else {
-                return <Typography component="span" sx={{ fontSize: 'small' }}>{tag}</Typography>
+                return <Typography key={`${tag}-${i}`} component="span" sx={{ fontSize: 'small' }}>{tag}</Typography>
               }
             })}
           </Typography>  
diff --git a/strudel-demo-app/src/app/search-data-repositories/DataListPanel.tsx b/strudel-demo-app/src/pages/search-data-repositories/_components/DataListPanel.tsx
similarity index 91%
rename from strudel-demo-app/src/app/search-data-repositories/DataListPanel.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/_components/DataListPanel.tsx
index 7e11db38..0de42ffa 100644
--- a/strudel-demo-app/src/app/search-data-repositories/DataListPanel.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/_components/DataListPanel.tsx
@@ -3,8 +3,8 @@ import SortIcon from '@mui/icons-material/Sort';
 import { Box, Button, Pagination, Paper, Stack, TextField, Typography } from '@mui/material';
 import React from 'react';
 import { DataListCard } from './DataListCard';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setSearch } from './context/actions';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
+import { setSearch } from '../_context/actions';
 
 interface DataListPanelProps {
   onToggleFiltersPanel: () => any
@@ -64,7 +64,7 @@ export const DataListPanel: React.FC<DataListPanelProps> = (props) => {
       >
         <Stack flex={1}>
           {state.filteredData?.map((item, i) => (
-            <DataListCard item={item} />
+            <DataListCard key={item[state.dataIdField]} item={item} />
           ))}
         </Stack>
         {!state.previewItem && (
diff --git a/strudel-demo-app/src/app/search-data-repositories/FiltersPanel.tsx b/strudel-demo-app/src/pages/search-data-repositories/_components/FiltersPanel.tsx
similarity index 87%
rename from strudel-demo-app/src/app/search-data-repositories/FiltersPanel.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/_components/FiltersPanel.tsx
index 9ea787ba..41fcb531 100644
--- a/strudel-demo-app/src/app/search-data-repositories/FiltersPanel.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/_components/FiltersPanel.tsx
@@ -1,13 +1,13 @@
 import { Stack } from '@mui/material';
 import { DatePicker } from '@mui/x-date-pickers';
 import React from 'react';
-import { CheckboxList } from '../../components/CheckboxList';
-import { FilterField } from '../../components/FilterField';
-import { Filters } from '../../components/Filters';
-import { StrudelSlider } from '../../components/StrudelSlider';
-import { FilterConfig, FilterOperator } from '../../types/filters.types';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setFilter } from './context/actions';
+import { CheckboxList } from '../../../components/CheckboxList';
+import { FilterField } from '../../../components/FilterField';
+import { Filters } from '../../../components/Filters';
+import { StrudelSlider } from '../../../components/StrudelSlider';
+import { FilterConfig, FilterOperator } from '../../../types/filters.types';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
+import { setFilter } from '../_context/actions';
 
 interface FiltersPanelProps {
   onClose: () => any
diff --git a/strudel-demo-app/src/app/search-data-repositories/PreviewPanel.tsx b/strudel-demo-app/src/pages/search-data-repositories/_components/PreviewPanel.tsx
similarity index 93%
rename from strudel-demo-app/src/app/search-data-repositories/PreviewPanel.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/_components/PreviewPanel.tsx
index 56923cd9..06961f23 100644
--- a/strudel-demo-app/src/app/search-data-repositories/PreviewPanel.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/_components/PreviewPanel.tsx
@@ -2,9 +2,9 @@ import React from 'react';
 import { Box, Button, IconButton, Link, Paper, Stack, Typography } from '@mui/material';
 import CloseIcon from '@mui/icons-material/Close';
 import { Link as RouterLink } from 'react-router-dom';
-import { LabelValueTable } from '../../components/LabelValueTable';
+import { LabelValueTable } from '../../../components/LabelValueTable';
 import { DataGrid } from '@mui/x-data-grid';
-import { useSearchDataRepositories } from './context/ContextProvider';
+import { useSearchDataRepositories } from '../_context/ContextProvider';
 import { GridColDef } from '@mui/x-data-grid';
 
 interface PreviewPanelProps {
@@ -67,9 +67,9 @@ export const PreviewPanel: React.FC<PreviewPanelProps> = (props) => {
             <Typography>
               {state.previewItem[state.cardFields.tags].map((tag: string, i: number) => {
                 if (state.cardFields.tags && i < state.previewItem[state.cardFields.tags].length - 1) {
-                  return <span>{`${tag}, `}</span>
+                  return <span key={`${tag}-${i}`}>{`${tag}, `}</span>
                 } else {
-                  return <span>{tag}</span>
+                  return <span key={`${tag}-${i}`}>{tag}</span>
                 }
               })}
             </Typography>
diff --git a/strudel-demo-app/src/app/search-data-repositories/definitions.json b/strudel-demo-app/src/pages/search-data-repositories/_config/definitions.json
similarity index 100%
rename from strudel-demo-app/src/app/search-data-repositories/definitions.json
rename to strudel-demo-app/src/pages/search-data-repositories/_config/definitions.json
diff --git a/strudel-demo-app/src/app/search-data-repositories/context/ContextProvider.tsx b/strudel-demo-app/src/pages/search-data-repositories/_context/ContextProvider.tsx
similarity index 100%
rename from strudel-demo-app/src/app/search-data-repositories/context/ContextProvider.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/_context/ContextProvider.tsx
diff --git a/strudel-demo-app/src/app/search-data-repositories/context/actions.ts b/strudel-demo-app/src/pages/search-data-repositories/_context/actions.ts
similarity index 100%
rename from strudel-demo-app/src/app/search-data-repositories/context/actions.ts
rename to strudel-demo-app/src/pages/search-data-repositories/_context/actions.ts
diff --git a/strudel-demo-app/src/app/search-data-repositories/SearchDataRepositoriesWrapper.tsx b/strudel-demo-app/src/pages/search-data-repositories/_layout.tsx
similarity index 78%
rename from strudel-demo-app/src/app/search-data-repositories/SearchDataRepositoriesWrapper.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/_layout.tsx
index a968a455..a3c9d11e 100644
--- a/strudel-demo-app/src/app/search-data-repositories/SearchDataRepositoriesWrapper.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/_layout.tsx
@@ -3,15 +3,15 @@ import React from 'react';
 import { Outlet } from 'react-router-dom';
 import { TopBar } from '../../components/TopBar';
 import { useDataFromSource } from '../../utils/useDataFromSource';
-import { basename } from '../App';
-import { SearchDataRepositoriesProvider } from './context/ContextProvider';
-import definitions from './definitions.json';
+import { SearchDataRepositoriesProvider } from './_context/ContextProvider';
+import definitions from './_config/definitions.json';
+import { basename } from '../../main';
 
 /**
  * Top-level wrapper for the search-data-repositories Task Flow templates.
  * Inner pages are rendered inside the `<Outlet />` component
  */
-export const SearchDataRepositoriesWrapper: React.FC = () => {
+const SearchDataRepositoriesLayout: React.FC = () => {
   // strudel-kit-variable-next-line
   const datasets = useDataFromSource('default/search-data-repositories/datasets.json', basename)
 
@@ -36,4 +36,6 @@ export const SearchDataRepositoriesWrapper: React.FC = () => {
       </Box>
     </Box>
   )
-}
\ No newline at end of file
+}
+
+export default SearchDataRepositoriesLayout;
\ No newline at end of file
diff --git a/strudel-demo-app/src/app/search-data-repositories/DatasetExplorer.tsx b/strudel-demo-app/src/pages/search-data-repositories/index.tsx
similarity index 83%
rename from strudel-demo-app/src/app/search-data-repositories/DatasetExplorer.tsx
rename to strudel-demo-app/src/pages/search-data-repositories/index.tsx
index e9ee7f56..1f51c9f6 100644
--- a/strudel-demo-app/src/app/search-data-repositories/DatasetExplorer.tsx
+++ b/strudel-demo-app/src/pages/search-data-repositories/index.tsx
@@ -1,17 +1,17 @@
 import { Box, Grid } from '@mui/material';
 import React, { useState } from 'react';
 import { PageHeader } from '../../components/PageHeader';
-import { DataListPanel } from './DataListPanel';
-import { FiltersPanel } from './FiltersPanel';
-import { PreviewPanel } from './PreviewPanel';
-import { useSearchDataRepositories } from './context/ContextProvider';
-import { setPreviewItem } from './context/actions';
+import { DataListPanel } from './_components/DataListPanel';
+import { FiltersPanel } from './_components/FiltersPanel';
+import { PreviewPanel } from './_components/PreviewPanel';
+import { useSearchDataRepositories } from './_context/ContextProvider';
+import { setPreviewItem } from './_context/actions';
 
 /**
  * The main explore page for the search-data-repositories Task Flow.
  * Displays a page header, `<FiltersPanel>`, `<DataListPanel>`, and `<PreviewPanel>`.
  */
-export const DatasetExplorer: React.FC = () => {
+const DatasetExplorer: React.FC = () => {
   const {state, dispatch} = useSearchDataRepositories();
   const [showFiltersPanel, setShowFiltersPanel] = useState(true);
 
@@ -71,4 +71,6 @@ const getMainColumnSize = (showFiltersPanel: boolean, showPreviewPanel: boolean)
   } else if (showFiltersPanel && showPreviewPanel) {
     return 6;
   }
-};
\ No newline at end of file
+};
+
+export default DatasetExplorer;
\ No newline at end of file
diff --git a/strudel-demo-app/src/router.ts b/strudel-demo-app/src/router.ts
new file mode 100644
index 00000000..588c0466
--- /dev/null
+++ b/strudel-demo-app/src/router.ts
@@ -0,0 +1,41 @@
+// Generouted, changes to this file will be overriden
+/* eslint-disable */
+
+import { components, hooks, utils } from '@generouted/react-router/client'
+
+export type Path =
+  | `/`
+  | `/compare-data`
+  | `/compare-data/compare`
+  | `/compare-data/new`
+  | `/contribute-data`
+  | `/contribute-data/new`
+  | `/contribute-data/portal`
+  | `/contribute-data/review`
+  | `/explore-data`
+  | `/explore-data/:id`
+  | `/monitor-activities`
+  | `/monitor-activities/calendar`
+  | `/monitor-activities/detail`
+  | `/run-computation`
+  | `/run-computation/:id/data-inputs`
+  | `/run-computation/:id/results`
+  | `/run-computation/:id/running`
+  | `/run-computation/:id/settings`
+  | `/search-data-repositories`
+  | `/search-data-repositories/:id`
+
+export type Params = {
+  '/explore-data/:id': { id: string }
+  '/run-computation/:id/data-inputs': { id: string }
+  '/run-computation/:id/results': { id: string }
+  '/run-computation/:id/running': { id: string }
+  '/run-computation/:id/settings': { id: string }
+  '/search-data-repositories/:id': { id: string }
+}
+
+export type ModalPath = never
+
+export const { Link, Navigate } = components<Path, Params>()
+export const { useModals, useNavigate, useParams } = hooks<Path, Params, ModalPath>()
+export const { redirect } = utils<Path, Params>()
diff --git a/strudel-demo-app/src/app/theme.tsx b/strudel-demo-app/src/theme.tsx
similarity index 100%
rename from strudel-demo-app/src/app/theme.tsx
rename to strudel-demo-app/src/theme.tsx
diff --git a/strudel-demo-app/vite.config.ts b/strudel-demo-app/vite.config.ts
index 5a33944a..2a9bd588 100644
--- a/strudel-demo-app/vite.config.ts
+++ b/strudel-demo-app/vite.config.ts
@@ -1,7 +1,8 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import generouted from '@generouted/react-router/plugin';
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  plugins: [react()],
+  plugins: [react(), generouted()],
 })


Test patch:
diff --git a/strudel-demo-app/src/app/App.test.tsx b/strudel-demo-app/src/app/App.test.tsx
deleted file mode 100644
index 2a68616d..00000000
--- a/strudel-demo-app/src/app/App.test.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import App from './App';
-
-test('renders learn react link', () => {
-  render(<App />);
-  const linkElement = screen.getByText(/learn react/i);
-  expect(linkElement).toBeInTheDocument();
-});