diff --git a/src/catalog.js b/src/catalog.js
new file mode 100644
index 0000000000000000000000000000000000000000..aaf76270325c93dcfbb4ae6ac0fd9b77f8cf28b7
--- /dev/null
+++ b/src/catalog.js
@@ -0,0 +1,53 @@
+/*
+ * List .json files found in root or first subdirectory level and
+ * return them as a mirador catalog
+ * This code works for webpack dev server and caddy (as file server), and is not expected
+ * to function for other webservers
+ */
+
+export default {
+  // url: relative or absolute path
+  // depth: maximum depth for search of .json (use 0 to search only at root, 1 for first-level folders, etc)
+  // subfolder: only for internal usage
+  get_initial_catalog: function(url = '/data', depth=1, subfolder='') {
+    if (depth < 0) {
+      return;
+    }
+    const req_init = {headers: {'Accept': 'application/json'}};
+    return fetch(`${url}/${subfolder}`, req_init)
+      .then(response => {
+        if (!response.ok) {
+          throw new Error(`failed to list manifests from ${url}, http response code: ${reponse.status}`);
+        }
+        return response.json();
+      })
+      .then(async (response_json) => {
+        // handling caddy response, which is like
+        // [{name: "first.json", ...}, {name: "second.json", ...}, ...]
+        if (response_json.length >= 1 && response_json[0].hasOwnProperty('name')) {
+          response_json = response_json.map(e => e.name.replace(/\/$/, ''));
+        }
+
+        // assume all files which don't end in .json are folder
+        let subfolders_content = await Promise.allSettled(response_json
+          .filter(e => !e.endsWith('.json'))
+          .map(folder => this.get_initial_catalog(url, depth - 1, folder))
+        );
+
+        // filter files from current folder
+        // & append files from subfolders
+        let items = response_json
+          .filter(e => e.endsWith('.json'))
+          .concat(subfolders_content
+            // get only successfull queries & with a content
+            .filter(p => p.status === "fulfilled" && p.value != null)
+            .map(p => p.value)
+          ).flat();
+
+        // prepend with original url only if we are at the root folder,
+        // else prepend only with current folder
+        return items
+          .map(e => subfolder == '' ? `${url}/${e}` : `${subfolder}/${e}`);
+      });
+  }
+}
diff --git a/src/index.js b/src/index.js
index 8c210912fc7adfb078a8ab382f551327af02ddd6..0e7931ea82fa0d3c84289299e36d6c459b6934e1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,6 @@
 import Mirador from 'mirador/dist/es/src/index';
+import LocalCatalog from './catalog.js';
+import * as actions from 'mirador/dist/es/src/state/actions';
 import annotationPlugins from 'mirador-annotations/es/index';
 import LocalStorageAdapter from 'mirador-annotations/es/LocalStorageAdapter';
 import AnnototAdapter from 'mirador-annotations/es/AnnototAdapter';
@@ -25,4 +27,16 @@ const config = {
   ]
 };
 
-Mirador.viewer(config, [...annotationPlugins]);
+let viewer = Mirador.viewer(config, [...annotationPlugins]);
+var store = viewer.store;
+console.info('store: ', store);
+
+LocalCatalog.get_initial_catalog()
+  .then(catalog => {
+    console.debug('loading local catalog: ', catalog);
+
+    catalog.forEach(manifest => 
+      // setTimeout avoid UI freeze
+      setTimeout(() => store.dispatch(actions.addResource(manifest)), 0)
+    );
+  })