diff --git a/CHANGELOG.md b/CHANGELOG.md
index d53a831e79105e4a8f39270e7cdc09867bef75aa..5ef6c2a114c1ca5c8dd4440e0e7e122444d6d8e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [Unreleased]
 
 ### Added (for new features, dependecies etc.)
+- added preview for tif images
 
 * new function `form_elements.make_alert` which generates a proceed/cancel
   dialog which intercepts a function call and asks the user for confirmation.
diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index a64d39fec41232a8fd889e4b61679b0ffb15ec9c..33567a800f7f18449228c21e3a65d723093d7538 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -45,6 +45,8 @@ BUILD_MODULE_EXT_PREVIEW=ENABLED
 BUILD_MODULE_EXT_RESOLVE_REFERENCES=ENABLED
 BUILD_MODULE_EXT_SSS_MARKDOWN=DISABLED
 BUILD_MODULE_EXT_TRIGGER_CRAWLER_FORM=DISABLED
+BUILD_MODULE_EXT_BOTTOM_LINE=DISABLED
+BUILD_MODULE_EXT_BOTTOM_LINE_TIFF_PREVIEW=DISABLED
 
 ##############################################################################
 # Navbar properties
diff --git a/libs/UTIF-8205c1f.zip b/libs/UTIF-8205c1f.zip
new file mode 100644
index 0000000000000000000000000000000000000000..7069cd288d5d629a7fc3a03d8d92415e78aeb728
Binary files /dev/null and b/libs/UTIF-8205c1f.zip differ
diff --git a/libs/pako-dummy.zip b/libs/pako-dummy.zip
new file mode 100644
index 0000000000000000000000000000000000000000..e493ee9d673c81a523ad8e488c509c392765da52
Binary files /dev/null and b/libs/pako-dummy.zip differ
diff --git a/makefile b/makefile
index 3bf4e967fd8581eb6678a3d6546c8b83e3d71ad7..41c7cf51acaf47caa38a5306af924633801c130e 100644
--- a/makefile
+++ b/makefile
@@ -40,7 +40,7 @@ SRC_EXT_DIR = $(abspath src/ext)
 LIBS_DIR = $(abspath libs)
 TEST_CORE_DIR = $(abspath test/core/)
 TEST_EXT_DIR = $(abspath test/ext)
-LIBS = fonts css/bootstrap.css js/bootstrap.js js/state-machine.js js/jquery.js js/showdown.js js/dropzone.js css/dropzone.css js/loglevel.js js/leaflet.js css/leaflet.css css/images js/leaflet-latlng-graticule.js js/proj4.js js/proj4leaflet.js js/leaflet-coordinates.js css/leaflet-coordinates.css js/leaflet-graticule.js js/bootstrap-select.js css/bootstrap-select.css js/bootstrap-autocomplete.min.js js/plotly.js
+LIBS = fonts css/bootstrap.css js/bootstrap.js js/state-machine.js js/jquery.js js/showdown.js js/dropzone.js css/dropzone.css js/loglevel.js js/leaflet.js css/leaflet.css css/images js/leaflet-latlng-graticule.js js/proj4.js js/proj4leaflet.js js/leaflet-coordinates.js css/leaflet-coordinates.css js/leaflet-graticule.js js/bootstrap-select.js css/bootstrap-select.css js/bootstrap-autocomplete.min.js js/plotly.js js/pako.js js/utif.js
 
 TEST_LIBS = $(LIBS) js/qunit.js css/qunit.css $(subst $(TEST_CORE_DIR)/,,$(shell find $(TEST_CORE_DIR)/))
 
@@ -265,6 +265,12 @@ $(LIBS_DIR)/js/bootstrap-autocomplete.min.js: unzip $(LIBS_DIR)/js
 $(LIBS_DIR)/js/plotly.js: unzip $(LIBS_DIR)/js
 	ln -s $(LIBS_DIR)/plotly.js-1.52.2/dist/plotly.min.js $@
 
+$(LIBS_DIR)/js/pako.js: unzip $(LIBS_DIR)/js
+	ln -s $(LIBS_DIR)/pako-dummy/pako.js $@
+
+$(LIBS_DIR)/js/utif.js: unzip $(LIBS_DIR)/js
+	ln -s $(LIBS_DIR)/UTIF-8205c1f/UTIF.js $@
+
 
 $(addprefix $(LIBS_DIR)/, js css):
 	mkdir $@ || true
diff --git a/src/core/js/ext_bottom_line.js b/src/core/js/ext_bottom_line.js
index 1bcf73dea17889bda9858ed78a2f6848e1dc21ee..4f563f049da55a532c663350cc2b4ca50e09104a 100644
--- a/src/core/js/ext_bottom_line.js
+++ b/src/core/js/ext_bottom_line.js
@@ -42,8 +42,9 @@
  * @requires load_config (function from caosdb.js)
  * @requires getEntityPath (function from caosdb.js)
  * @requires connection (module from webcaosdb.js)
+ * @requires UTIF (from utif.js library)
  */
-var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntityPath, connection) {
+var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntityPath, connection, UTIF) {
 
     /**
      * @property {string|function} create - a function with one parameter
@@ -103,7 +104,75 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit
      */
     const _create_video_preview = function(entity) {
         var path = connection.getFileSystemPath() + getEntityPath(entity);
-        return $(`<div class="caosdb-v-bottom-line-video-preview"><video controls="controls"><source src="${path}"/></video></div>`)[0];
+        return $(`<div class="caosdb-v-bottom-line-video-preview">
+          <video controls="controls"><source src="${path}"/></video></div>`)[0];
+    }
+
+    /**
+     * Error class which has the special to_html method.
+     *
+     * The to_html method creates a html representation of the error which is
+     * intended for displaying in the bottom_line container.
+     */
+    const BottomLineError = function(arg) {
+        this._is_bottom_line_error = true;
+
+        if (arg.message) {
+            // arg is an Error object
+            this.message = arg.message;
+            this.stack = arg.stack;
+        } else {
+            this.message = arg;
+        }
+
+        this.to_html = function() {
+            return $(`<div><p>An error occured while loading this preview.<p>${
+              this.message}<div>`)[0];
+        }
+    }
+
+    /**
+     * Create a preview for tiff files.
+     *
+     * Tiff files are decompressed if necessary and converted into png by UTIF library.
+     *
+     * @param {HTMLElement} entity
+     * @return {Promise for HTMLElement} Promise for an IMG element.
+     */
+    const _create_tiff_preview = function(entity) {
+        const path = connection.getFileSystemPath() + getEntityPath(entity);
+        const result = $(`<div class="caosdb-v-bottom-line-image-preview"></div>`);
+        const img = $(`<img src="${path}"/>`)[0];
+        result.append(img);
+
+        /**
+         * Promise which retrieves the tiff file and calls the UTIF library for
+         * decompression and conversion into png.
+         */
+        return new Promise((resolve, reject) => {
+            const xhr = new XMLHttpRequest();
+            UTIF._xhrs.push(xhr);
+            UTIF._imgs.push(img);
+            xhr.open("GET", path);
+            xhr.responseType = "arraybuffer";
+            xhr.onload = (e) => {
+                try {
+                  // decompress and convert tiff file
+                  UTIF._imgLoaded(e);
+
+                  // return the result if no error occured.
+                  resolve(result);
+                } catch(err) {
+                  // throw errors from UTIF to the awaiting caller.
+                  reject(new BottomLineError(err));
+                }
+            }
+            // throw http errors to the awaiting caller.
+            xhr.onerror = reject;
+
+            // this finally triggers the retrieval
+            xhr.send();
+        });
     }
 
     /**
@@ -119,6 +188,8 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit
 
     var fallback_preview = undefined;
 
+    const _tiff_preview_enabled = "${BUILD_MODULE_EXT_BOTTOM_LINE_TIFF_PREVIEW}" == "ENABLED";
+
     /**
      * Default creators.
      *
@@ -128,7 +199,12 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit
             id: "_default_creators.pictures",
             is_applicable: (entity) => _path_has_file_extension(
                 entity, ["jpg", "png", "gif", "svg"]),
-            create: _create_picture_preview
+            create: _create_picture_preview,
+        }, {
+            id: "_default_creators.tiff_images",
+            is_applicable: (entity) => _tiff_preview_enabled && _path_has_file_extension(
+                entity, ["tif", "tiff","dng","cr2","nef"]),
+            create: _create_tiff_preview,
         }, { // videos
             id: "_default_creators.videos",
             is_applicable: (entity) => _path_has_file_extension(
@@ -231,8 +307,10 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit
             }
         } catch (err) {
             logger.error(err);
-            const err_msg = "An error occured while loading this preview";
-            set_preview_container(entity, err_msg);
+            if (!err._is_bottom_line_error) {
+              err = new BottomLineError(err);
+            }
+            set_preview_container(entity, err.to_html());
         }
     }
 
@@ -460,8 +538,9 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit
         _css_class_preview_container,
         _css_class_preview_container_button,
         _css_class_preview_container_resolvable,
+        BottomLineError: BottomLineError,
     }
-}($, log.getLogger("ext_bottom_line"), resolve_references.is_in_viewport_vertically, load_config, getEntityPath, connection);
+}($, log.getLogger("ext_bottom_line"), resolve_references.is_in_viewport_vertically, load_config, getEntityPath, connection, UTIF);
 
 
 /**
@@ -525,7 +604,7 @@ var plotly_preview = function(logger, ext_bottom_line, plotly) {
 
 // this will be replaced by require.js in the future.
 $(document).ready(function() {
-    if ("${BUILD_MODULE_EXT_BOTTOM_LINE}" === "ENABLED") {
+    if ("${BUILD_MODULE_EXT_BOTTOM_LINE}" == "ENABLED") {
         caosdb_modules.register(plotly_preview);
         caosdb_modules.register(ext_bottom_line);
     }
diff --git a/src/core/xsl/main.xsl b/src/core/xsl/main.xsl
index efbf2e6b6e4db0ad7b14cb1c2483157bc79001f1..91f75733d7746c9e99bf5501a3cbb77155cddea3 100644
--- a/src/core/xsl/main.xsl
+++ b/src/core/xsl/main.xsl
@@ -142,6 +142,16 @@
         <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/js/webcaosdb.js')"/>
       </xsl:attribute>
     </xsl:element>
+    <xsl:element name="script">
+      <xsl:attribute name="src">
+        <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/js/pako.js')"/>
+      </xsl:attribute>
+    </xsl:element>
+    <xsl:element name="script">
+      <xsl:attribute name="src">
+        <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/js/utif.js')"/>
+      </xsl:attribute>
+    </xsl:element>
     <script>
       $(document).ready(() => paging.initPaging(window.location.href, <xsl:value-of select="/Response/@count"/> ));
     </script>
diff --git a/test/core/index.html b/test/core/index.html
index 50d8cbef8003d6fb9ab383f94405bcfa07270774..c57f6c1f46dd5ff18c6d3508d135c96b10d7b198 100644
--- a/test/core/index.html
+++ b/test/core/index.html
@@ -37,6 +37,8 @@
   <script src="js/bootstrap.js"></script>
   <script src="js/bootstrap-select.js"></script>
   <script src="js/bootstrap-autocomplete.min.js"></script>
+  <script src="js/utif.js"></script>
+  <script src="js/pako.js"></script>
   <script src="js/webcaosdb.js"></script>
   <script src="js/plotly.js"></script>
   <script>
diff --git a/test/core/js/modules/ext_bottom_line.js b/test/core/js/modules/ext_bottom_line.js.js
similarity index 90%
rename from test/core/js/modules/ext_bottom_line.js
rename to test/core/js/modules/ext_bottom_line.js.js
index 21c92167271f87554a9af881554d33db686d9194..a26cc2188eacef210d1acc9eb7de59c53f06b3e7 100644
--- a/test/core/js/modules/ext_bottom_line.js
+++ b/test/core/js/modules/ext_bottom_line.js.js
@@ -67,7 +67,7 @@ var ext_bottom_line_test_suite = function ($, ext_bottom_line, QUnit) {
     });
 
     QUnit.test("_creators", function (assert) {
-        assert.equal(ext_bottom_line._creators.length, 7, "seven creators");
+        assert.equal(ext_bottom_line._creators.length, 8, "eight creators");
     });
 
     QUnit.test("add_preview_container", function(assert) {
@@ -105,7 +105,7 @@ var ext_bottom_line_test_suite = function ($, ext_bottom_line, QUnit) {
                     assert.equal(container.text(), "blablabla");
                     break;
                 case "error":
-                    assert.equal(container.text(), "An error occured while loading this preview");
+                    assert.equal(container.text(), "An error occured while loading this preview.Test Error");
                     break;
                 case "load-forever":
                     assert.equal(container.text(), "Please wait...");
@@ -121,4 +121,12 @@ var ext_bottom_line_test_suite = function ($, ext_bottom_line, QUnit) {
 
     });
 
+    QUnit.test("tiff converter", async function(assert) {
+        let entity_xml = `<Response><File path="../pics/saturn.tif"/></Response>`;
+        const entity = (await transformation.transformEntities(str2xml(entity_xml)))[0];
+        const tiff_preview = await ext_bottom_line._creators.filter((c) => c.id == "_default_creators.tiff_images")[0].create(entity);
+
+        assert.equal($(tiff_preview).find("img").attr("src").slice(0,21), "data:image/png;base64", "decoded tiff to png");
+    });
+
 }($, ext_bottom_line, QUnit);
diff --git a/test/core/pics/saturn.tif b/test/core/pics/saturn.tif
new file mode 100644
index 0000000000000000000000000000000000000000..f226ebab7fca3c9caa306f78d80eaa7b4068f994
Binary files /dev/null and b/test/core/pics/saturn.tif differ