diff --git a/src/repdav/textgrid_named_dav_provider.py b/src/repdav/textgrid_named_dav_provider.py
new file mode 100644
index 0000000000000000000000000000000000000000..92fd92a79552bcddf5658dfb5903bc4d841088f6
--- /dev/null
+++ b/src/repdav/textgrid_named_dav_provider.py
@@ -0,0 +1,154 @@
+import io
+import logging
+import threading
+from pprint import pformat
+
+from tgclients.config import TextgridConfig
+from tgclients.crud import TextgridCRUD
+from tgclients.metadata import TextgridMetadata
+from wsgidav.util import join_uri
+
+from repdav.stream_tools import FileLikeQueue
+from repdav.textgrid_dav_provider import (TextgridAggregation, TextgridProject,
+                                          TextgridResource,
+                                          TextgridResourceProvider,
+                                          TextgridRoot)
+
+_logger = logging.getLogger(__name__)
+
+# ============================================================================
+# TextgridNamedResourceProvider
+# ============================================================================
+class TextgridNamedResourceProvider(TextgridResourceProvider):
+    """DAV provider that serves Textgrid resources."""
+
+    def get_resource_inst(self, path, environ):
+        _logger.debug(
+            "Called TextgridNamedResourceProvider.get_resource_inst(self, %s, %s).",
+            path,
+            pformat(environ),
+        )
+        self._count_get_resource_inst += 1
+        root = TextgridNamedRoot("/", environ)
+        # an instance of _DAVResource (i.e. either DAVCollection or DAVNonCollection)
+        return root.resolve("/", path)
+
+
+class TextgridNamedRoot(TextgridRoot):
+    """Top level collection that incorporates Textgrid projects.
+
+    This is implemented as READ-ONLY as projects may not be created with webDAV.
+    """
+
+    def __init__(self, path, environ):
+        super().__init__(path, environ)
+
+    def get_member(self, name):
+        _logger.debug("Called TextgridRoot.get_member(self, %s).", name)
+        return TextgridNamedProject(join_uri(self.path, name), self.environ)
+
+class TextgridNamedProject(TextgridProject):
+    def __init__(self, path, environ):
+        _logger.debug("Called TextgridNamedProject.__init__(self, %s, environ).", path)
+        super().__init__(path, environ)
+        self._tgmeta = TextgridMetadata()
+
+    def get_member_names(self):
+        _logger.debug("Called TextgridNamedProject.get_member_names(self).")
+        # names = []
+        # the item keys are textgrid uris but we want the resource titles only
+        # for _, member_dict in self._resources.items():
+        #     names.append(member_dict.get("title"))
+        # return names
+        #
+        # path resolution has to be rewritten before we can work with resource titles
+        response = self._tgsearch.list_project_root(self.path.split("/")[-1], self._sid)
+        names = []
+        for result in response.result:
+            names.append(self._tgmeta.filename_from_metadata(result))
+        return names
+
+    def get_member(self, name):
+        _logger.debug("Called TextgridNamedProject.get_member(self, %s).", name)
+        tguri = 'textgrid:' + self._tgmeta.id_from_filename(name)
+        response = self._tgsearch.info(tguri, self._sid)
+        info = {
+            name: {
+                "title": response.result[0].object_value.generic.provided.title,
+                "format": response.result[0].object_value.generic.provided.format,
+                "extent": response.result[0].object_value.generic.generated.extent,
+            }
+        }
+        _logger.info("INFO: %s", info)
+        if "aggregation" in info[name]["format"]:
+            return TextgridNamedAggregation(join_uri(self.path, name), self.environ, info, self._tgmeta)
+        return TextgridNamedResource(join_uri(self.path, name), self.environ, info, self._tgmeta)
+
+class TextgridNamedAggregation(TextgridAggregation):
+    def __init__(self, path, environ, info, tgmeta):
+        _logger.debug(
+            "Called TextgridNamedAggregation.__init__(self, %s, environ, info).", path
+        )
+        super().__init__(path, environ, info)
+        self._tgmeta = tgmeta
+
+    def get_member_names(self):
+        _logger.debug("Called TextgridNamedAggregation.get_member_names(self).")
+        tguri = 'textgrid:' + self._tgmeta.id_from_filename(self.path.split("/")[-1])
+        response = self._tgsearch.list_aggregation(tguri, self._sid)
+        names = []
+        for result in response.result:
+            names.append(self._tgmeta.filename_from_metadata(result))
+        return names
+
+    def get_member(self, name):
+        _logger.debug("Called TextgridNamedAggregation.get_member(self, %s).", name)
+        tguri = 'textgrid:' + self._tgmeta.id_from_filename(name)
+        response = self._tgsearch.info(tguri, self._sid)
+        info = {
+            name: {
+                "title": response.result[0].object_value.generic.provided.title,
+                "format": response.result[0].object_value.generic.provided.format,
+                "extent": response.result[0].object_value.generic.generated.extent,
+            }
+        }
+        _logger.info("INFO: %s", info)
+        if "aggregation" in info[name]["format"]:
+            return TextgridNamedAggregation(join_uri(self.path, name), self.environ, info, self._tgmeta)
+        return TextgridNamedResource(join_uri(self.path, name), self.environ, info, self._tgmeta)
+
+class TextgridNamedResource(TextgridResource):
+    """Non-Aggregation resources."""
+
+    def __init__(self, path, environ, info, tgmeta):
+        _logger.debug("Called TextgridNamedResource.__init__(self, %s, environ).", path)
+        super().__init__(path, environ, info)
+        self._tgmeta = tgmeta
+        config = TextgridConfig()
+        self._crud = TextgridCRUD(config.crud)
+
+    def get_content(self):
+        _logger.debug("Called TextgridNamedResource.get_content(self) with path:", self.path)
+        tguri = 'textgrid:' + self._tgmeta.id_from_filename(self.path.split("/")[-1])
+
+        _logger.debug("crud-read: " +  tguri)
+        return io.BytesIO(self._crud.read_data(tguri, self._sid).content)
+
+    def begin_write(self, content_type=None):
+        _logger.debug(
+            "Called TextgridResource.begin_write(self, content_type=%s).", content_type
+        )
+
+        queue = FileLikeQueue(int(self._size))
+        tguri = 'textgrid:' + self._tgmeta.id_from_filename(self.path.split("/")[-1])
+        metadata = self._crud.read_metadata(tguri, self._sid).content
+
+        def worker():
+            _logger.debug("Called TextgridResource.begin_write.worker().")
+            self._crud.update_resource(self._sid, tguri, queue, metadata)
+
+        thread = threading.Thread(target=worker)
+        thread.setDaemon(True)
+        thread.start()
+        self.upload_thread = thread
+        return queue