diff --git a/DESCRIPTION b/DESCRIPTION
index 56285669ae65a495ba3a52c32f5254f12a9d7a7b..2d488afb8ec8eff5616b2dd19f820a51d6ce52f6 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -6,7 +6,7 @@ Authors@R: c(
     person(given = "Arsenij", family = "Ustjanzew", email = "arsenij.ustjanzews@mpi-bn.mpg.de", role = c("aut", "cre")),
     person(given = "Jens", family = "Preussner", email = "jens.preussner@mpi-bn.mpg.de", role = c("aut"), comment = c(ORCID = "0000-0003-1927-3458")),
     person(given = "Mario", family = "Looso", email = "mario.looso@mpi-bn.mpg.de", role = "aut"))
-Description: What the package does (one paragraph).
+Description: The i2dash.scrnaseq R package is an extension for the core i2dash package, which supplies several plotting methods embedded in components and pre-defined pages with linked components that enables to create an i2dashboard object with focus on single cell RNA-seq analysis.
 Depends: R (>= 3.5.2)
 License: What license is it under?
 Encoding: UTF-8
@@ -21,6 +21,11 @@ Imports:
     tidyr,
     dplyr,
     SummarizedExperiment,
-    SingleCellExperiment
+    SingleCellExperiment,
+    shinyWidgets,
+    Seurat,
+    magrittr
 Suggests: 
-    kableExtra
+    kableExtra,
+    multipanelfigure,
+    ComplexHeatmap
diff --git a/NAMESPACE b/NAMESPACE
index aa384cc0adad05bb1d524b21ba0449dc53745712..5f3224fa2e5b2a8585e0ba1296fc355f7f6e2f56 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -3,12 +3,14 @@
 export("%<>%")
 export("%>%")
 export(ComplexHeatmap_heatmap)
+export(bubbleplot)
 export(plotly_barplot)
 export(plotly_boxplot)
+export(plotly_bubbleplot)
 export(plotly_scatterplot)
 export(plotly_violinplot)
 exportMethods(add_dimred_feature_page)
-exportMethods(add_dimred_metadata_page)
+exportMethods(add_dimred_sample_page)
 exportMethods(add_feature_expression_page)
 exportMethods(add_feature_grid_page)
 exportMethods(barplot)
diff --git a/R/AllGenerics.R b/R/AllGenerics.R
index db2d1f41ef3a5cec6760c4536f7c28a19a821860..2e6944b47ab2297244b5137707c3bde06d9d4c9f 100644
--- a/R/AllGenerics.R
+++ b/R/AllGenerics.R
@@ -7,37 +7,56 @@ NULL
 #'
 #' Users can select features and a dimension reduction to plot feature expression values.
 #'
-#' @param report A \linkS4class{i2dash::i2dashboard} report.
+#' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
 #' @param object A \linkS4class{SingleCellExperiment::SingleCellExperiment} object or a \linkS4class{Seurat::Seurat} object.
-#' @param use_dimred A list of data.frames (matrices) or a single data.frame (matrix) containing coordinates of the reduced dimensions, a character vector representing valid \code{reducedDim} slots of \code{object} or names of the \linkS4class{Seurat::DimReduc} object in \code{object@reductions}.
-#' @param exprs_values A data.frame (matrix) containing expression data of features of interest in rows and samples in columns, or a string representing the name of an \code{assay} of \code{object}.
+#' @param use_dimred Coordinates of the reduced dimensions, used for the scatterplot (see Details).
+#' @param exprs_values Expression data of features of interest in rows and samples in columns (see Details).
+#' @param page The name of the page to be added.
 #' @param assay A character vector specifying which assay from \code{object@assays} to obtain expression values from.
 #' @param slot A character vector specifying the name of the slot in the assay.
 #' @param subset_row A character vector (of feature names), a logical vector or numeric vector (of indices) specifying the features to use. The default of NULL will use all features.
 #' @param title The title of the page.
 #' @param menu (Optional) The name of the menu, under which the page should appear.
 #'
+#' @details The parameters \code{use_dimred}, \code{exprs_values} (or \code{assay}) and \code{group_by} take different arguments depending on the class of \code{object}.
+#'   In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
+#'   In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
+#'   \itemize{
+#'     \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
+#'     \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
+#'     \item column names of \code{colData(object)} or \code{object@meta.data}.
+#'   }
 #' @name feature-grid-page
 #' @rdname feature-grid-page
 #' @exportMethod add_feature_grid_page
-setGeneric("add_feature_grid_page", function(report, object, ...) standardGeneric("add_feature_grid_page"))
+setGeneric("add_feature_grid_page", function(dashboard, object, ...) standardGeneric("add_feature_grid_page"))
 
-#' Add a dimension reduction page with feature metadata
+#' View a dimension reduction side-by-side with feature metadata
 #'
-#' This function adds a page with two linked components to the \code{dashboard} object: A scatterplot, showing samples in along two-dimensional coordinates, and a table, showing feature metadata. A click on a feature in the table updates the scatterplot with the feature expression.
+#' The dimension reduction plot is colored by feature expression and updated if the users clicks feature rows in the metadata table.
 #'
 #' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
 #' @param object A \linkS4class{SingleCellExperiment::SingleCellExperiment} object or a \linkS4class{Seurat::Seurat} object.
-#' @param use_dimred A list of data.frames (matrices) or a single data.frame (matrix) containing coordinates of the reduced dimensions, a character vector representing valid \code{reducedDim} slots of \code{object} or names of the \linkS4class{Seurat::DimReduc} object in \code{object@reductions}.
-#' @param exprs_values A data.frame (matrix) containing expression data of features of interest in rows and samples in columns, or a string representing the name of an \code{assay} of \code{object}.
-#' @param feature_metadata A data.frame (matrix) along rows of \code{exprs_values} containing feature metadata, or a character vector indicating columns from \code{rowData(object)} or \code{object@meta.data}.
-#' @param assay A character vector specifying which assay from \code{object@assays} to obtain expression values from.
-#' @param slot A character vector specifying the name of the slot in the assay.
+#' @param use_dimred Coordinates of the reduced dimensions, used for the scatterplot (see Details).
+#' @param exprs_values Expression data of features of interest in rows and samples in columns (see Details).
+#' @param feature_metadata A data.frame (matrix) along rows of \code{exprs_values} containing feature metadata, or a character vector indicating columns from \code{rowData(object)} or \code{object[[assay]]@feature.data}.
+#' @param page The name of the page to be added.
+#' @param assay A character specifying the assay (\code{object@assays}) to obtain expression values from. (Default: "RNA")
+#' @param assay_slot A character specifying the name of the data slot in the assay. (Default: "data")
 #' @param title The title of the page.
 #' @param menu (Optional) The name of the menu, under which the page should appear.
 #'
-#' @name dimred-metadata-page
-#' @rdname dimred-metadata-page
+#' @details The parameters \code{use_dimred}, \code{exprs_values} (or \code{assay}) and \code{group_by} take different arguments depending on the class of \code{object}.
+#'   In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
+#'   In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
+#'   \itemize{
+#'     \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
+#'     \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
+#'     \item column names of \code{colData(object)} or \code{object@meta.data}.
+#'   }
+#'
+#' @name dimred-feature-page
+#' @rdname dimred-feature-page
 #' @exportMethod add_dimred_feature_page
 setGeneric("add_dimred_feature_page", function(dashboard, object, ...) standardGeneric("add_dimred_feature_page"))
 
@@ -59,14 +78,16 @@ setGeneric("summarize_samples", function(object, ...) standardGeneric("summarize
 #' @exportMethod summarize_features
 setGeneric("summarize_features", function(object, ...) standardGeneric("summarize_features"))
 
-#' Add a gene expression page.
+#' Add a feature expression page.
 #'
 #' This function adds a page with two linked components to the \code{dashboard} object: A scatterplot, showing samples in along two-dimensional coordinates, and a violin plot, showing feature expression values by groups defined in \code{group_by}.
 #'
 #' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
+#' @param page The name of the page to be added.
 #' @param use_dimred Coordinates of the reduced dimensions, used for the scatterplot (see Details).
 #' @param exprs_values Expression data of features of interest in rows and samples in columns (see Details).
 #' @param assay A character vector specifying which assay from \code{object@assays} to obtain expression values from (see Details).
+#' @param assay_slot A character specifying the name of the data slot in the assay. (Default: "data")
 #' @param group_by Data along samples that is used for grouping expression values in the violin plot (see Details).
 #' @param object A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object.
 #' @param slot A character vector specifying the name of the slot in the assay.
@@ -79,7 +100,7 @@ setGeneric("summarize_features", function(object, ...) standardGeneric("summariz
 #'   In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
 #'   In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
 #'   \itemize{
-#'     \item the name of a item in \code{reducedDims(object)} or \code{object@reductions},
+#'     \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
 #'     \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
 #'     \item column names of \code{colData(object)} or \code{object@meta.data}.
 #'   }
@@ -92,10 +113,11 @@ setGeneric("add_feature_expression_page", function(dashboard, object, ...) stand
 #' Renders a component containing a vertical violin plot
 #'
 #' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
-#' @param y A data.frame (matrix) containing numeric observations for the vertical axis, or a character vector indicating column names of \code{colData(object)}, \code{rowData(object)}.
-#' @param object A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.
-#' @param group_by An optional data.frame (matrix) with columns containing grouping factors for the horizontal axis.
-#' @param use A character specifying where to obtain the data from. One of \code{"colData"} or \code{"rowData"}.
+#' @param object An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.
+#' @param y A data.frame (matrix) containing numeric observations for the vertical axisor a character vector indicating the columns to use from \code{use}.
+#' @param group_by An optional data.frame (matrix) with columns containing grouping factors for the horizontal axis or a character vector indicating the columns to use from \code{use}.
+#' @param use A character specifying where to obtain the data from. Valid input for SingleCellExperiment object: ("colData", "rowData"). Valid input for Seurat object: ("meta.data" for sample metadata, "meta.feature" for feature metadata.)
+#' @param assay Necessery, if \code{use} = "meta.feature". A character defining the assay to obtain the feature metadata from (default "RNA").
 #' @param title The title of the component.
 #' @param y_title The title of the y-axis.
 #' @param group_by_title The title of the x-axis.
@@ -108,10 +130,11 @@ setGeneric("violinplot", function(dashboard, object, ...) standardGeneric("violi
 #' Renders a component containing a horizontal barplot.
 #'
 #' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
-#' @param y_group_by A data.frame (matrix) with columns containing grouping factors for the vertical axis.
-#' @param object A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.
-#' @param x_group_by Optionally provide a data.frame (matrix) with columns containing grouping factors for the horizontal axis. The result is a barplot grouped by the levels in \code{x_group_by} and shows the relative number of its observations.
-#' @param use A character specifying where to obtain the data from. One of \code{"colData"} or \code{"rowData"}.
+#' @param object An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.
+#' @param y_group_by A data.frame (matrix) with columns containing grouping factors for the vertical axis or a character vector indicating the columns to use from \code{use}.
+#' @param x_group_by Optionally provide a data.frame (matrix) with columns containing grouping factors for the horizontal axis or a character vector indicating the columns to use from \code{use}. The result is a barplot grouped by the levels in \code{x_group_by}, which shows the relative number of its observations.
+#' @param use A character specifying where to obtain the data from. Valid input for SingleCellExperiment object: ("colData", "rowData"). Valid input for Seurat object: ("meta.data" for sample metadata, "meta.feature" for feature metadata.)
+#' @param assay Necessery, if \code{use} = "meta.feature". A character defining the assay to obtain the feature metadata from (default "RNA").
 #' @param title The title of the components junk.
 #' @param y_group_by_title The title of the y-axis.
 #' @param x_group_by_title The title of the x-axis.
@@ -124,10 +147,11 @@ setGeneric("barplot", function(dashboard, object, ...) standardGeneric("barplot"
 #' Renders a component containing a boxplot
 #'
 #' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
-#' @param x A data.frame (matrix) containing numeric observations for the horizontal axis, or a character vector indicating column names of \code{colData(object)}, \code{rowData(object)}.
-#' @param object A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.
-#' @param group_by An optional data.frame (matrix) with columns containing grouping factors for the vertical axis.
-#' @param use A character specifying where to obtain the data from. One of \code{"colData"} or \code{"rowData"}.
+#' @param object An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.
+#' @param x A data.frame (matrix) containing numeric observations for the horizontal axis or a character vector indicating the columns to use from \code{use}.
+#' @param group_by An optional data.frame (matrix) with columns containing grouping factors for the vertical axis or a character vector indicating the columns to use from \code{use}
+#' @param use A character specifying where to obtain the data from. Valid input for SingleCellExperiment object: ("colData", "rowData"). Valid input for Seurat object: ("meta.data" for sample metadata, "meta.feature" for feature metadata.)
+#' @param assay Necessery, if \code{use} = "meta.feature". A character defining the assay to obtain the feature metadata from (default "RNA").
 #' @param title The title of the components junk.
 #' @param x_title The title of the x-axis.
 #' @param group_by_title The title of the y-axis.
@@ -137,20 +161,71 @@ setGeneric("barplot", function(dashboard, object, ...) standardGeneric("barplot"
 #' @exportMethod boxplot
 setGeneric("boxplot", function(dashboard, object, ...) standardGeneric("boxplot"))
 
+#' Characterize and visualize dimension reductions and sample groupings / metadata.
+#'
+#' Creates a page with up to four different linked components, including a scatterplot for dimension reductions, a bar plot showing numbers of observations by group, and a silhouette plot to assess grouping consistency.
+#' Additional sample metadata is visualized using boxplots and barplots, depending on the data type of the underlying variable.
+#'
+#' @param dashboard A \linkS4class{i2dash::i2dashboard}.
+#' @param object An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.
+#' @param use_dimred A data.frame (matrix) containing coordinates of the reduced dimensions or a string indicating a dimension reduction from "reductions" of a Seurat \code{object}. Rownames are used as sample labels.
+#' @param sample_metadata Sample metadata in columns and samples in rows (see Details).
+#' @param group_by A string indicating a column in \code{metadata} that is used to group observations.
+#' @param page The name of the page to be added.
+#' @param title The title of the page.
+#' @param labels An optional vector with sample labels.
+#' @param show_group_sizes A logical value indicating if a barplot showing the number of observations from \code{group_by} will be created (default \code{TRUE}).
+#' @param show_silhouette A logical value indicating if a silhouette plot should be shown (default \code{FALSE}).
+#' @param menu The name of the menu, under which the page should appear.
+#'
+#' @details The parameters \code{use_dimred}, \code{sample_metadata} (or \code{assay}) and \code{group_by} take different arguments depending on the class of \code{object}.
+#'   In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
+#'   In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
+#'   \itemize{
+#'     \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
+#'     \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
+#'     \item column names of \code{colData(object)} or \code{object@meta.data}.
+#'   }
+#' @name dimred-sample-page
+#' @rdname dimred-sample-page
+#' @exportMethod add_dimred_sample_page
+setGeneric("add_dimred_sample_page", function(dashboard, object, ...) standardGeneric("add_dimred_sample_page"))
+
 #' Renders a component containing a scatterplot with optional selection options
 #'
 #' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
-#' @param x A data.frame (matrix) containing columns with numeric values that will be mapped to the x-axis.
-#' @param y A data.frame (matrix) containing columns with numeric values that will be mapped to the y-axis.
-#' @param object A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.
-#' @param use A character specifying where to obtain the data from. One of \code{"colData"}, \code{"rowData"}, \code{"reducedDim"}.
-#' @param reducedDim A character vector indicating the reduced dimension to use from \code{"reducedDim"}
-#' @param colour_by An optional data.frame (matrix) containing columns with numeric or factorial values that will be used for colouring.
+#' @param object An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.
+#' @param x Values that will be mapped to the x-axis (see Details).
+#' @param y Values that will be mapped to the y-axis (see Details).
+#' @param use A character specifying where to obtain the data from \code{object} (see Details).
+#' @param use_dimred A character vector indicating the reduced dimension to use from \code{"object"} (see Details).
+#' @param assay A character defining the assay of \code{object} and is used for obtaining the \code{exprs_values} (default "RNA") (see Details).
+#' @param slot A character defining the data slot of \code{assay}.
+#' @param colour_by Numeric or factorial values that will be used for colouring.
 #' @param labels An optional vector with sample names. A dropdown menu for colouring by label will be provided.
-#' @param exprs_values An optional data.frame (matrix) containing expression data of features of interest in rows and samples in columns.
+#' @param exprs_values Expression data of features of interest in rows and samples in columns (see Details).
 #' @param title The title of the components junk.
 #' @param x_title An optional title of the x-axis. If not provided the column names from \code{x} are used instead.
 #' @param y_title An optional title of the y-axis. If not provided the column names from \code{y}  are used instead.
+#' @param plot_title An optional title of the plot.
+#'
+#' @details The parameters \code{x}, \code{y}, \code{colour_by}, \code{use}, \code{use_dimred}, \code{exprs_values}, \code{assay} and \code{slot}) take different arguments depending on the class of \code{object}.
+#'   In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters \code{x}, \code{y}, \code{colour_by} and \code{exprs_values} are expected to be of class \code{data.frame} or \code{matrix}. T he parameters \code{x}, \code{y} can also be a numeric vector. The parameters \code{use}, \code{use_dimred}, \code{assay} and \code{slot} can be ignored.
+#'   In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} object is supplied, the parameters are expected to be of class \code{character}:
+#'   \itemize{
+#'     \item \code{use} "colData", "rowData", "reducedDim",
+#'     \item \code{use_dimred} the name of an item in \code{reducedDims(object)},
+#'     \item \code{exprs_values} a valid assay name from \code{assayNames(object)},
+#'     \item \code{colour_by} column names of \code{colData(object)} or \code{colData(object)} in dependence of \code{use}.
+#'   }
+#'   In case a \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}:
+#'   \itemize{
+#'     \item \code{use} "meta.data" for sample metadata, "meta.feature" for feature metadata, "reduction" for a dimension reduction,
+#'     \item \code{use_dimred} the name of an item in \code{object@reductions},
+#'     \item \code{assay} a valid assay name from \code{names(object@assays)},
+#'     \item \code{slot} a valid data slot from \code{assay},
+#'     \item \code{colour_by} column names of \code{use}.
+#'   }
 #'
 #' @name scatterplot
 #' @rdname scatterplot
@@ -171,29 +246,11 @@ setGeneric("scatterplot", function(dashboard, object, ...) standardGeneric("scat
 #' @param cluster_columns A logical controls whether to make cluster on columns.
 #' @param clustering_method Method to perform hierarchical clustering, passed to \link[stats]{hclust}.
 #' @param clustering_distance The distance measure to use for hierarchical clustering.
+#' @param show_column_labels A logical controls whether the column lables should be displayed. Pay attention that a large number of columns with column lables can cause visualization problems.
+#' @param column_title The column title of the heatmap.
+#' @param row_title The row title of the heatmap.
 #'
 #' @name heatmap
 #' @rdname heatmap
 #' @exportMethod heatmap
 setGeneric("heatmap", function(dashboard, object, ...) standardGeneric("heatmap"))
-
-#' Characterize and visualize dimension reductions and sample groupings / metadata.
-#'
-#' Creates a page with up to four different linked components, including a scatterplot for dimension reductions, a bar plot showing numbers of observations by group, and a silhouette plot to assess grouping consistency.
-#' Additional sample metadata is visualized using boxplots and barplots, depending on the data type of the underlying variable.
-#'
-#' @param dashboard A \linkS4class{i2dash::i2dashboard}.
-#' @param use_dimred A data.frame (matrix) containing coordinates of the reduced dimensions. Rownames are used as sample labels.
-#' @param metadata A data.frame (matrix) containing metadata (e.g. cluster, timepoint, number of features, etc) along samples.
-#' @param group_by A string indicating a column in \code{metadata} that is used to group observations.
-#' @param title The title of the page.
-#' @param labels An optional vector with sample labels.
-#' @param show_group_sizes A logical value indicating if a barplot showing the number of observations from \code{group_by} will be creaed (default \code{TRUE}).
-#' @param show_silhouette A logical value indicating if a silhouette plot should be shown (default \code{FALSE}).
-#' @param menu The name of the menu, under which the page should appear.
-#'
-#' @name dimred-metadata-page
-#' @rdname dimred-metadata-page
-#' @exportMethod add_dimred_metadata_page
-setGeneric("add_dimred_metadata_page", function(dashboard, object, ...) standardGeneric("add_dimred_metadata_page"))
-
diff --git a/R/barplot.R b/R/barplot.R
index 633a29713d124d63d99d80e52832900d46b06694..ac364637843e303ba8dda9ec910784229f532a41 100644
--- a/R/barplot.R
+++ b/R/barplot.R
@@ -14,6 +14,16 @@ setMethod("barplot",
               assertive.types::assert_is_any_of(x_group_by, c("data.frame", "matrix"))
               if(is.null(colnames(x_group_by))) colnames(x_group_by) <- paste0("V", 1:ncol(x_group_by))
               if(nrow(y_group_by) != nrow(x_group_by)) stop("The numbers of rows in 'x_group_by' and 'y_group_by' are not equal.")
+              # Columns are swapped in case of equal column names to prevent visualization of the same column (always the first one) on both axes.
+              if(ncol(x_group_by) > 1){
+                if(colnames(y_group_by)[1] == colnames(x_group_by)[1]) {
+                  if(ncol(x_group_by) > 2) {
+                    x_group_by <-  x_group_by[, c(2, 1, c(3:ncol(x_group_by)))]
+                  } else {
+                    x_group_by <-  x_group_by[, c(2, 1)]
+                  }
+                }
+              }
             }
 
             # Create component environment
@@ -37,13 +47,13 @@ setMethod("barplot",
             return(expanded_component)
           })
 
-
 #' @rdname barplot
 #' @return An object of class \linkS4class{i2dash::i2dashboard}.
 #' @export
 setMethod("barplot",
           signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
-          function(dashboard, object, use = "colData", y_group_by = NULL, x_group_by = NULL, ...) {
+          function(dashboard, object, use = c("colData", "rowData"), y_group_by = NULL, x_group_by = NULL, ...) {
+            use <- match.arg(use)
             if(use == "colData") {
               if(!is.null(y_group_by)) {
                 assertive.sets::assert_is_subset(y_group_by, colnames(SummarizedExperiment::colData(object)))
@@ -82,3 +92,49 @@ setMethod("barplot",
                     x_group_by = x_group_by,
                     ...)
           })
+
+#' @rdname barplot
+#' @return An object of class \linkS4class{i2dash::i2dashboard}.
+#' @export
+setMethod("barplot",
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard, object, use = c("meta.data", "meta.features"), assay = "RNA", y_group_by = NULL, x_group_by = NULL, ...) {
+            use <- match.arg(use)
+            if(use == "meta.data") {
+              if(!is.null(y_group_by)) {
+                assertive.sets::assert_is_subset(y_group_by, colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!y_group_by) -> y_group_by
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> y_group_by
+              }
+              if(!is.null(x_group_by)) {
+                assertive.sets::assert_is_subset(x_group_by,  colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!x_group_by) -> x_group_by
+              }
+            } else if (use == "meta.features") {
+              if(!is.null(y_group_by)) {
+                assertive.sets::assert_is_subset(y_group_by, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!y_group_by) -> y_group_by
+              } else {
+                object[[assay]]@meta.features %>%
+                  as.data.frame() -> y_group_by
+              }
+              if(!is.null(x_group_by)) {
+                assertive.sets::assert_is_subset(x_group_by, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!x_group_by) -> x_group_by
+              }
+            }
+            barplot(dashboard,
+                    y_group_by = y_group_by,
+                    x_group_by = x_group_by,
+                    ...)
+          })
diff --git a/R/boxplot.R b/R/boxplot.R
index 03444ba2afe16c758996fc5d96444a98f4f95114..01cee446f1b6318d516303aea7c153c12e2a86e0 100644
--- a/R/boxplot.R
+++ b/R/boxplot.R
@@ -37,13 +37,13 @@ setMethod("boxplot",
             return(expanded_component)
           })
 
-
 #' @rdname boxplot
 #' @return An object of class \linkS4class{i2dash::i2dashboard}.
 #' @export
 setMethod("boxplot",
           signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
-          function(dashboard, object, use = "colData", x = NULL, group_by = NULL, title = NULL, x_title = NULL, group_by_title = NULL) {
+          function(dashboard, object, use = c("colData", "rowData"), x = NULL, group_by = NULL, ...) {
+            use <- match.arg(use)
             if(use == "colData") {
               if(!is.null(x)) {
                 assertive.sets::assert_is_subset(x, colnames(SummarizedExperiment::colData(object)))
@@ -80,8 +80,51 @@ setMethod("boxplot",
             boxplot(dashboard,
                     x = x,
                     group_by = group_by,
-                    title = title,
-                    x_title = x_title,
-                    group_by_title = group_by_title)
+                    ...)
           })
 
+#' @rdname boxplot
+#' @return An object of class \linkS4class{i2dash::i2dashboard}.
+#' @export
+setMethod("boxplot",
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard, object, use = c("meta.data", "meta.features"), assay = "RNA", x = NULL, group_by = NULL, ...) {
+            use <- match.arg(use)
+            if(use == "meta.data") {
+              if(!is.null(x)) {
+                assertive.sets::assert_is_subset(x, colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!x) -> x
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> x
+              }
+              if(!is.null(group_by)) {
+                assertive.sets::assert_is_subset(group_by,  colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!group_by) -> group_by
+              }
+            } else if (use == "meta.features") {
+              if(!is.null(x)) {
+                assertive.sets::assert_is_subset(x, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!x) -> x
+              } else {
+                object[[assay]]@meta.features %>%
+                  as.data.frame() -> x
+              }
+              if(!is.null(group_by)) {
+                assertive.sets::assert_is_subset(group_by, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!group_by) -> group_by
+              }
+            }
+            boxplot(dashboard,
+                    x = x,
+                    group_by = group_by,
+                    ...)
+          })
diff --git a/R/bubbleplot.R b/R/bubbleplot.R
new file mode 100644
index 0000000000000000000000000000000000000000..10521ca1cf9ce6a5c7699a9abe68bdc030bb7260
--- /dev/null
+++ b/R/bubbleplot.R
@@ -0,0 +1,73 @@
+#' Renders a component containing a bubbleplot with optional selection options
+#'
+#' @param dashboard An object of class \linkS4class{i2dash::i2dashboard}.
+#' @param x A data.frame (matrix) containing columns with numeric values that will be mapped to the x-axis.
+#' @param y A data.frame (matrix) containing columns with numeric values that will be mapped to the y-axis.
+#' @param size A ata.frame (matrix) containing columns with numeric values that describe the size of the bubbles.
+#' @param colour_by An optional data.frame (matrix) containing columns with numeric or factorial values that will be used for colouring.
+#' @param labels An optional vector with sample names.
+#' @param title The title of the components junk.
+#' @param x_title An optional title of the x-axis. If not provided the column names from \code{x} are used instead.
+#' @param y_title An optional title of the y-axis. If not provided the column names from \code{y}  are used instead.
+#' @return A string containing markdown code for the rendered component
+#' @export
+bubbleplot <- function(dashboard, x, y, size, colour_by = NULL, labels = NULL, title = NULL, x_title = NULL, y_title = NULL) {
+  # Validate input
+  assertive.types::assert_is_any_of(x, c("data.frame", "matrix"))
+  assertive.types::assert_is_any_of(y, c("data.frame", "matrix"))
+  assertive.types::assert_is_any_of(size, c("data.frame", "matrix"))
+
+  # select columns only containing numeric or integer values
+  x %<>%
+    as.data.frame() %>%
+    dplyr::select_if(function(col) is.integer(col) | is.numeric(col))
+  y %<>%
+    as.data.frame() %>%
+    dplyr::select_if(function(col) is.integer(col) | is.numeric(col))
+  size %<>%
+    as.data.frame() %>%
+    dplyr::select_if(function(col) is.integer(col) | is.numeric(col))
+
+  # provide column names
+  if(is.null(colnames(x))) colnames(x) <- paste0("X_", 1:ncol(x))
+  if(is.null(colnames(y))) colnames(y) <- paste0("Y_", 1:ncol(y))
+  if(is.null(colnames(size))) colnames(size) <- paste0("Size_", 1:ncol(size))
+
+  # check correct dimensions
+  if(nrow(x) != nrow(y)) stop("The number of rows in 'x' and 'y' is not equal.")
+  if(nrow(x) != nrow(size)) stop("The number of rows in 'x' and 'size' is not equal.")
+
+  # check optional parameters
+  if(!is.null(colour_by)){
+    assertive.types::assert_is_any_of(colour_by, c("data.frame", "matrix"))
+    colour_by %<>%
+      as.data.frame() %>%
+      dplyr::select_if(function(col) is.integer(col) | is.numeric(col) | is.factor(col))
+    if(is.null(colnames(colour_by))) colnames(colour_by) <- paste0("Color_", 1:ncol(colour_by))
+    if(nrow(x) != nrow(colour_by)) stop("The number of rows in 'x' and 'colour_by' is not equal.")
+  }
+  if(!is.null(labels)) assertive.types::assert_is_any_of(labels, "vector")
+  if(!is.null(labels)) assertive.types::is_character(title)
+  if(!is.null(labels)) assertive.types::is_character(x_title)
+  if(!is.null(labels)) assertive.types::is_character(y_title)
+
+  # Create random env id
+  env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
+
+  # Create component environment
+  env <- new.env()
+  env$x <- x
+  env$y <- y
+  env$size <- size
+  env$colour_by <- colour_by
+  env$labels <- labels
+  env$x_title <- x_title
+  env$y_title <- y_title
+
+  saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
+
+  # Expand component
+  timestamp <- Sys.time()
+  expanded_component <- knitr::knit_expand(file = system.file("templates", "bubbleplot.Rmd", package = "i2dash.scrnaseq"), title = title, env_id = env_id, date = timestamp)
+  return(expanded_component)
+}
diff --git a/R/dimension_reduction_page.R b/R/dimension_reduction_page.R
deleted file mode 100644
index 396017142b0571c3ff2e00fdfe51bd6f66218aa1..0000000000000000000000000000000000000000
--- a/R/dimension_reduction_page.R
+++ /dev/null
@@ -1,62 +0,0 @@
-#' @name dimred-metadata-page
-#' @rdname dimred-metadata-page
-#' @aliases add_dimred_metadata_page
-#' @return An object of class \linkS4class{i2dash::i2dashboard}.
-#' @export
-setMethod("add_dimred_metadata_page",
-          signature = signature(dashboard = "i2dashboard", object = "missing"),
-          function(dashboard, use_dimred, metadata, group_by, title = "Dimension reduction", labels = NULL, show_group_sizes = TRUE, show_silhouette = FALSE, menu = NULL) {
-  # Create random env id
-  env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
-
-  # Validate input
-  assertive.types::assert_is_any_of(use_dimred, c("data.frame", "matrix"))
-  assertive.types::assert_is_any_of(metadata, c("data.frame", "matrix"))
-  assertive.types::assert_is_any_of(group_by, c("character"))
-
-  assertive.sets::assert_is_subset(group_by, colnames(metadata))
-
-  # Create component environment
-  env <- new.env()
-  env$reduced_dim <- use_dimred[, 1:2]
-  env$metadata <- metadata
-  env$grouping <- group_by
-  env$labels <- labels
-  env$multiple_meta <- ncol(metadata) > 1
-
-  # save environment object
-  saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
-
-  expanded_components <- list()
-  timestamp <- Sys.time()
-
-  # Add dimension reduction component
-  dim_reduction <- knitr::knit_expand(file = system.file("templates", "dimension_reduction_page", "scatterplot_dimred.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
-  expanded_components <- append(expanded_components, dim_reduction)
-  free_comps <- 3
-
-  if(show_group_sizes){
-    barplot_grouping_component <- knitr::knit_expand(file = system.file("templates", "dimension_reduction_page", "barplot_group_sizes.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
-    expanded_components <- append(expanded_components, barplot_grouping_component)
-    free_comps <- free_comps - 1
-  }
-
-  if(show_silhouette){
-    silhouette_plot_component <- knitr::knit_expand(file = system.file("templates", "dimension_reduction_page", "silhouette_plot.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
-    expanded_components <- append(expanded_components, silhouette_plot_component)
-    free_comps <- free_comps - 1
-  }
-
-  grouping_index <- which(colnames(metadata) == group_by)
-  remaining_metadata <- colnames(metadata)[-grouping_index][1:free_comps]
-  for(i in remaining_metadata){
-    meta_component <- knitr::knit_expand(file = system.file("templates", "dimension_reduction_page", "metadata_plot.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp, meta_column = i)
-    expanded_components <- append(expanded_components, meta_component)
-  }
-
-  # Expand component
-  timestamp <- Sys.time()
-
-  dashboard@pages[["dim_reduction_page"]] <- list(title = title, layout = "2x2_grid", menu = menu, components = expanded_components, max_components = 4, sidebar = NULL)
-  return(dashboard)
-})
diff --git a/R/dimred_metadata_page.R b/R/dimred_feature_page.R
similarity index 55%
rename from R/dimred_metadata_page.R
rename to R/dimred_feature_page.R
index 45b703ece06d36ed55a13b560ccced282a59f4c1..9d0555f407cfd497e0f616638ea6c962839fda5b 100644
--- a/R/dimred_metadata_page.R
+++ b/R/dimred_feature_page.R
@@ -1,18 +1,20 @@
-#' @name dimred-metadata-page
-#' @rdname dimred-metadata-page
+#' @name dimred-feature-page
+#' @rdname dimred-feature-page
 #' @aliases add_dimred_feature_page
+#' @return An object of class \linkS4class{i2dash::i2dashboard}.
 #' @export
 setMethod("add_dimred_feature_page",
           signature = signature(dashboard = "i2dashboard", object = "missing"),
-          function(dashboard, use_dimred, exprs_values, feature_metadata, title = "Marker gene expression", menu = NULL) {
+          function(dashboard, use_dimred, exprs_values, feature_metadata, page = "dimred_feature_page", title = "Dim. reduction & deature metadata", menu = NULL) {
+
+            page %>% tolower %>% gsub(x = ., pattern = " ", replacement = "_") %>% make.names -> name
 
             # Create random env id
             env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
 
             # Input validation
-            exprs_values <- as.matrix(exprs_values)
             assertive.types::assert_is_any_of(use_dimred, c("data.frame", "matrix"))
-            assertive.types::assert_is_any_of(exprs_values, c("data.frame", "matrix"))
+            exprs_values <- as.matrix(exprs_values)
             assertive.types::assert_is_any_of(feature_metadata, c("data.frame", "matrix"))
 
             if(ncol(use_dimred) < 2 ) stop("'use_dimred' should contain at least two columns.")
@@ -21,8 +23,6 @@ setMethod("add_dimred_feature_page",
               colnames(feature_metadata) <- paste0("V", 1:ncol(feature_metadata))
             }
 
-            if(is.null(rownames(exprs_values))) rownames(exprs_values) <- 1:nrow(exprs_values)
-
             # Create component environment
             env <- new.env()
 
@@ -35,22 +35,22 @@ setMethod("add_dimred_feature_page",
             # Render component
             timestamp <- Sys.time()
 
-            component <- knitr::knit_expand(file = system.file("templates", "dimred_feature_page.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
+            component <- knitr::knit_expand(file = system.file("templates", "dimred_metadata.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
 
-            dashboard@pages[["dimred_metadata_page"]] <- list(title = title, layout = "default", menu = menu, components = component, max_components = 1)
+            dashboard@pages[[name]] <- list(title = title, layout = "default", menu = menu, components = component, max_components = 1)
             return(dashboard)
           })
 
 
-#' @name dimred-metadata-page
-#' @rdname dimred-metadata-page
+#' @name dimred-feature-page
+#' @rdname dimred-feature-page
 #' @export
 setMethod("add_dimred_feature_page",
           signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
-          function(dashboard, object, use_dimred, exprs_values, feature_metadata, subset_row, title = "Marker gene expression", menu = NULL) {
+          function(dashboard, object, use_dimred, exprs_values, feature_metadata, subset_row, ...) {
 
             assertive.sets::assert_is_subset(use_dimred, SingleCellExperiment::reducedDimNames(object))
-            assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment:assay(object))
+            assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment::assayNames(object))
             assertive.sets::assert_is_subset(feature_metadata, colnames(SummarizedExperiment::rowData(object)))
 
             use_dimred <- SingleCellExperiment::reducedDim(object, use_dimred)
@@ -68,41 +68,43 @@ setMethod("add_dimred_feature_page",
             dashboard <- add_dimred_feature_page(dashboard = dashboard,
                                                use_dimred = use_dimred,
                                                exprs_values = exprs_values,
-                                               metadata = metadata,
-                                               title = title,
-                                               menu = menu)
-            return(dashboard)
+                                               feature_metadata = metadata,
+                                               ...)
           })
 
-#' @name dimred-metadata-page
-#' @rdname dimred-metadata-page
+#' @name dimred-feature-page
+#' @rdname dimred-feature-page
 #' @export
 setMethod("add_dimred_feature_page",
           signature = signature(dashboard = "i2dashboard", object = "Seurat"),
-          function(dashboard, object, use_dimred, exprs_values, feature_metadata, subset_row, assay, assay_slot = "data", title = "Marker gene expression", menu = NULL) {
-
-            assertive.sets::assert_is_subset(use_dimred, names(object@reductions))
-            assertive.sets::assert_is_subset(assay, names(object@assays))
-            assertive.sets::assert_is_subset(feature_metadata, names(object@meta.data))
-
+          function(dashboard, object, use_dimred, feature_metadata, subset_row, assay = "RNA", assay_slot = "data", ...){
+            assertive.types::assert_is_character(use_dimred)
+            assertive.types::assert_is_character(assay)
+            assertive.types::assert_is_character(assay_slot)
+            assertive.sets::assert_is_subset(use_dimred, colnames(object@reductions))
+            assertive.sets::assert_is_subset(assay, colnames(object@assays))
+            assertive.sets::assert_is_subset(feature_metadata, colnames(object[[assay]]@meta.features))
+
+            # exprs_values
             assay_obj <- Seurat::GetAssay(object = object, assay = assay)
-            expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)
-            metadata <- object@meta.data[metadata]
-
+            exprs_values <- Seurat::GetAssayData(object = assay_obj, slot = assay_slot)
             if(!is.null(subset_row)) {
-              expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)[subset_row, ]
-              metadata <- metadata[subset_row, ]
+              exprs_values <- exprs_values[subset_row, ]
             }
 
-            use_dimred <- lapply(use_dimred, function(dimred) {
-              Seurat::Embeddings(object, reduction = dimred)[, 1:2]
-            })
-
-            dashboard <- add_dimred_feature_page(dashboard = dashboard,
-                                               use_dimred = use_dimred,
-                                               exprs_values = expression,
-                                               metadata = metadata,
-                                               title = title,
-                                               menu = menu)
-            return(dashboard)
+            # feature_metadata
+            object[[assay]]@meta.features %>%
+              as.data.frame() %>%
+              dplyr::select(!!feature_metadata) -> feature_metadata
+            feature_metadata <- feature_metadata[subset_row, ]
+
+            # use_dimred
+            use_dimred <- Seurat::Embeddings(object, reduction = use_dimred)[, 1:2]
+
+            dashboard <- add_dimred_feature_page(
+              dashboard = dashboard,
+              use_dimred = use_dimred,
+              exprs_values = exprs_values,
+              feature_metadata = feature_metadata,
+              ...)
           })
diff --git a/R/dimred_sample_page.R b/R/dimred_sample_page.R
new file mode 100644
index 0000000000000000000000000000000000000000..d36368da7fe9f5aed76afa7ea51e63e2fa6347d5
--- /dev/null
+++ b/R/dimred_sample_page.R
@@ -0,0 +1,113 @@
+#' @name dimred-sample-page
+#' @rdname dimred-sample-page
+#' @aliases add_dimred_sample_page
+#' @return An object of class \linkS4class{i2dash::i2dashboard}.
+#' @export
+setMethod("add_dimred_sample_page",
+          signature = signature(dashboard = "i2dashboard", object = "missing"),
+          function(dashboard, use_dimred, sample_metadata, group_by, page = "dimred_sample_page", title = "Dim. reduction & sample metadata", labels = NULL, show_group_sizes = TRUE, show_silhouette = FALSE, menu = NULL) {
+
+            page %>% tolower %>% gsub(x = ., pattern = " ", replacement = "_") %>% make.names -> name
+
+            # Create random env id
+            env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
+
+            # Validate input
+            assertive.types::assert_is_any_of(use_dimred, c("data.frame", "matrix"))
+            assertive.types::assert_is_any_of(sample_metadata, c("data.frame", "matrix"))
+            assertive.types::assert_is_character(group_by)
+            assertive.sets::assert_is_subset(group_by, colnames(sample_metadata))
+            assertive.types::assert_is_factor(sample_metadata[[group_by]])
+
+            # Create component environment
+            env <- new.env()
+            env$reduced_dim <- use_dimred[, 1:2]
+            env$metadata <- sample_metadata
+            env$grouping <- group_by
+            env$labels <- labels
+            env$multiple_meta <- ncol(sample_metadata) > 1
+
+            # save environment object
+            saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
+
+            expanded_components <- list()
+            timestamp <- Sys.time()
+
+            # Add dimension reduction component
+            dim_reduction <- knitr::knit_expand(file = system.file("templates", "dimred_sample_page", "scatterplot_dimred.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
+            expanded_components <- append(expanded_components, dim_reduction)
+            free_comps <- 3
+
+            if(show_group_sizes){
+              barplot_grouping_component <- knitr::knit_expand(file = system.file("templates", "dimred_sample_page", "barplot_group_sizes.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
+              expanded_components <- append(expanded_components, barplot_grouping_component)
+              free_comps <- free_comps - 1
+            }
+
+            if(show_silhouette){
+              silhouette_plot_component <- knitr::knit_expand(file = system.file("templates", "dimred_sample_page", "silhouette_plot.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
+              expanded_components <- append(expanded_components, silhouette_plot_component)
+              free_comps <- free_comps - 1
+            }
+
+            grouping_index <- which(colnames(sample_metadata) == group_by)
+            remaining_metadata <- colnames(sample_metadata)[-grouping_index][1:free_comps]
+            for(i in remaining_metadata){
+              meta_component <- knitr::knit_expand(file = system.file("templates", "dimred_sample_page", "metadata_plot.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp, meta_column = i)
+              expanded_components <- append(expanded_components, meta_component)
+            }
+
+            # Expand component
+            timestamp <- Sys.time()
+
+            dashboard@pages[[name]] <- list(title = title, layout = "2x2_grid", menu = menu, components = expanded_components, max_components = 4, sidebar = NULL)
+            return(dashboard)
+          })
+
+#' @name dimred-sample-page
+#' @rdname dimred-sample-page
+#' @export
+setMethod("add_dimred_sample_page",
+          signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
+          function(dashboard, object, use_dimred, sample_metadata, ...){
+
+            assertive.sets::assert_is_subset(use_dimred, SingleCellExperiment::reducedDimNames(object))
+            assertive.sets::assert_is_subset(sample_metadata, colnames(SummarizedExperiment::colData(object)))
+
+            use_dimred <- SingleCellExperiment::reducedDim(object, use_dimred)[, 1:2]
+
+            SummarizedExperiment::colData(object) %>%
+              as.data.frame() %>%
+              dplyr::select(!!sample_metadata) -> sample_metadata
+
+            dashboard <- add_dimred_sample_page(
+              dashboard = dashboard,
+              use_dimred = use_dimred,
+              sample_metadata = sample_metadata,
+              ...
+            )
+          })
+
+#' @name dimred-sample-page
+#' @rdname dimred-sample-page
+#' @export
+setMethod("add_dimred_sample_page",
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard, object, use_dimred, sample_metadata, ...){
+
+            assertive.sets::assert_is_subset(use_dimred, names(object@reductions))
+            assertive.sets::assert_is_subset(sample_metadata, colnames(object@meta.data))
+
+            use_dimred <- Seurat::Embeddings(object, reduction = use_dimred)[, 1:2]
+
+            object@meta.data %>%
+              as.data.frame() %>%
+              dplyr::select(!!sample_metadata) -> sample_metadata
+
+            dashboard <- add_dimred_sample_page(
+              dashboard = dashboard,
+              use_dimred = use_dimred,
+              sample_metadata = sample_metadata,
+              ...
+            )
+          })
diff --git a/R/feature_expression_page.R b/R/feature_expression_page.R
index a77d851350075cf0cdd089b247963b144189619f..3c8102ecef65795a242b95707fae6cef88f61e39 100644
--- a/R/feature_expression_page.R
+++ b/R/feature_expression_page.R
@@ -67,9 +67,9 @@ setMethod("add_feature_expression_page",
             assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment::assayNames(object))
             assertive.sets::assert_is_subset(group_by, colnames(SummarizedExperiment::colData(object)))
 
-            expression <- SummarizedExperiment::assay(object, i = exprs_values)
-            if(!is.null(features)) {
-              expression <- SummarizedExperiment::assay(object, i = exprs_values)[features, ]
+            exprs_values <- SummarizedExperiment::assay(object, i = exprs_values)
+            if(!is.null(subset_row)) {
+              exprs_values <- exprs_values[subset_row, ]
             }
 
             SummarizedExperiment::colData(object) %>%
@@ -77,8 +77,8 @@ setMethod("add_feature_expression_page",
               dplyr::select(!!group_by) -> metadata
 
             add_feature_expression_page(dashboard,
-                                     use_dimred = SingleCellExperiment::reducedDim(object, dimred),
-                                     exprs_values = expression,
+                                     use_dimred = SingleCellExperiment::reducedDim(object, use_dimred),
+                                     exprs_values = exprs_values,
                                      group_by = metadata,
                                      labels = colnames(object),
                                      title = title,
@@ -97,9 +97,9 @@ setMethod("add_feature_expression_page",
             assertive.sets::assert_is_subset(group_by, names(object@meta.data))
 
             assay_obj <- Seurat::GetAssay(object = object, assay = assay)
-            expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)
+            exprs_values <- Seurat::GetAssayData(object = assay_obj, slot = slot)
             if(!is.null(subset_row)) {
-              expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)[feature, ]
+              exprs_values <- exprs_values[subset_row, ]
             }
 
             object@meta.data[metadata] %>%
@@ -107,8 +107,8 @@ setMethod("add_feature_expression_page",
               dplyr::select(!!group_by) -> metadata
 
             add_feature_expression_page(dashboard,
-                                     use_dimred = Seurat::Embeddings(object, reduction = dimred),
-                                     exprs_values = expression,
+                                     use_dimred = Seurat::Embeddings(object, reduction = use_dimred),
+                                     exprs_values = exprs_values,
                                      group_by = metadata,
                                      labels = colnames(expression),
                                      title = title,
diff --git a/R/feature_grid_page.R b/R/feature_grid_page.R
index 5e2f15f263e8fc2e8b1bc121472061eaaeb7ca43..31ac7ff6cbea2d217ef2309ad425a5a706ccbfc9 100644
--- a/R/feature_grid_page.R
+++ b/R/feature_grid_page.R
@@ -4,11 +4,13 @@
 #' @return An object of class \linkS4class{i2dash::i2dashboard}.
 #' @export
 setMethod("add_feature_grid_page",
-          signature = signature(report = "i2dashboard", object = "missing"),
-          function(report, use_dimred, exprs_values, title = "Feature grid", menu = "Tools") {
+          signature = signature(dashboard = "i2dashboard", object = "missing"),
+          function(dashboard, use_dimred, exprs_values, page = "feature_grid_page", title = "Feature grid", menu = "Tools") {
+
+            page %>% tolower %>% gsub(x = ., pattern = " ", replacement = "_") %>% make.names -> name
 
             # warn if no interactive mode is used
-            if(!report@interactive) warning("This page can only be used during interactive shiny sessions. Consider setting interactivity(report) <- TRUE.")
+            if(!dashboard@interactive) warning("This page can only be used during interactive shiny sessions. Consider setting interactivity(dashboard) <- TRUE.")
 
             # Create random env id
             env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
@@ -19,75 +21,73 @@ setMethod("add_feature_grid_page",
             if(!assertive.properties::has_names(use_dimred)) {
               names(use_dimred) <- paste0("dimred_", 1:length(use_dimred))
             }
-
-            assertive.types::assert_is_any_of(exprs_values, c("data.frame", "matrix"))
+            exprs_values <- as.matrix(exprs_values)
 
             # Create component environment
             env <- new.env()
             env$use_dimred <- use_dimred
             env$exprs_values <- exprs_values
 
-            saveRDS(env, file = file.path(report@datadir, paste0(env_id, ".rds")))
+            saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
 
             # Expand component
             timestamp <- Sys.time()
             multi_gene_expr_component <- knitr::knit_expand(file = system.file("templates", "feature_grid.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
 
-            report@pages[["gene_grid_page"]] <- list(title = title, layout = "empty", menu = menu, components = multi_gene_expr_component, max_components = 1, sidebar = NULL)
-            return(report)
+            dashboard@pages[[name]] <- list(title = title, layout = "empty", menu = menu, components = multi_gene_expr_component, max_components = 1, sidebar = NULL)
+            return(dashboard)
           })
 
 #' @name feature-grid-page
 #' @rdname feature-grid-page
 #' @export
 setMethod("add_feature_grid_page",
-          signature = signature(report = "i2dashboard", object = "SingleCellExperiment"),
-          function(report, object, use_dimred, exprs_values, subset_row = NULL, ...) {
+          signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
+          function(dashboard, object, use_dimred, exprs_values, subset_row = NULL, ...) {
 
             assertive.sets::assert_is_subset(use_dimred, SingleCellExperiment::reducedDimNames(object))
             assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment::assayNames(object))
 
+            exprs_values <- SummarizedExperiment::assay(object, i = exprs_values)
             if(!is.null(subset_row)) {
-              expression <- SummarizedExperiment::assay(object, i = exprs_values)[subset_row, ]
-            } else {
-              expression <- SummarizedExperiment::assay(object, i = exprs_values)
+              exprs_values <- exprs_values[subset_row, ]
             }
 
             use_dimreds <- lapply(use_dimred, function(dimred) {
               SingleCellExperiment::reducedDim(object, dimred)[, 1:2]
             })
 
-            report <- add_gene_grid_page(report = report,
+            dashboard <- add_feature_grid_page(dashboard = dashboard,
                                          use_dimred = use_dimreds,
-                                         exprs_values = expression,
+                                         exprs_values = exprs_values,
                                          ...)
-            return(report)
+            return(dashboard)
           })
 
 #' @name feature-grid-page
 #' @rdname feature-grid-page
 #' @export
 setMethod("add_feature_grid_page",
-          signature = signature(report = "i2dashboard", object = "Seurat"),
-          function(report, object, use_dimred, assay, slot = "data", subset_row = NULL) {
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard, object, use_dimred, assay, assay_slot = "data", subset_row = NULL, ...) {
 
             assertive.sets::assert_is_subset(use_dimred, names(object@reductions))
             assertive.sets::assert_is_subset(assay, names(object@assays))
 
             assay_obj <- Seurat::GetAssay(object = object, assay = assay)
-            expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)
+            exprs_values <- Seurat::GetAssayData(object = assay_obj, slot = assay_slot)
 
             if(!is.null(subset_row)) {
-              expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)[subset_row, ]
+              exprs_values <- exprs_values[subset_row, ]
             }
 
             use_dimreds <- lapply(use_dimred, function(dimred) {
               Seurat::Embeddings(object, reduction = dimred)[, 1:2]
             })
 
-            report <- add_gene_grid_page(report = report,
+            dashboard <- add_feature_grid_page(dashboard = dashboard,
                                          use_dimred = use_dimreds,
-                                         expression = expression,
+                                         exprs_values = exprs_values,
                                          ...)
-            return(report)
+            return(dashboard)
           })
diff --git a/R/features_by_factors.R b/R/features_by_factors.R
deleted file mode 100644
index 3ec6ded241cebd69948933d69dacd42cfebd9fa9..0000000000000000000000000000000000000000
--- a/R/features_by_factors.R
+++ /dev/null
@@ -1,44 +0,0 @@
-#' Renders a features by factor violin plot
-#'
-#' @param object A \linkS4class{i2dash::i2dashboard} object.
-#' @param title A title that will be displayed on top.
-#' @param x A list with the x-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.
-#' @param y A list with the y-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.(Needs to be categorial. Horizontal violinplots are not possible.)
-#'
-#' @return A string containing markdown code for the rendered textbox
-features_by_factors <- function(object, x, y, title = "Features by factor") {
-  # Create random env id
-  env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
-
-  # validate input, create environment variables, save environment object
-  .validate_input_features_by_factors(object@datadir, env_id, x, y)
-
-  timestamp <- Sys.time()
-  expanded_component <- knitr::knit_expand(file = system.file("templates", "features_by_factors_template.Rmd", package = "i2dash.scrnaseq"), title = title, env_id = env_id, date = timestamp)
-  return(expanded_component)
-}
-
-.validate_input_features_by_factors <- function(workdir, env_id, x, y) {
-  env <- new.env()
-  env$x_selection <- FALSE
-  env$y_selection <- FALSE
-
-  # Create lists if needed
-  if(!is.list(x)) x <- list(x = x)
-  if(!is.list(y)) y <- list(y = y)
-
-  # Check validity
-  if(!all(sapply(y, is.numeric))) stop("y should only contain numeric values.")
-  if(!all(sapply(x, is.factor))) stop("x should only contain factorial values.")
-
-  # Add objects to env
-  env$x <- x
-  env$x_selection <- length(env$x) > 1
-
-  env$y <- y
-  env$y_selection <- length(env$y) > 1
-
-  # save environment as rds-object
-  saveRDS(env, file = file.path(workdir, paste0(env_id, ".rds")))
-  print("validation TRUE")
-}
diff --git a/R/heatmap.R b/R/heatmap.R
index e1fb1b4e24bef105d089a8b953d16ee84cc6ce4d..f4ef4a9f5bb53077e9dfff3554e880d276ef6def 100644
--- a/R/heatmap.R
+++ b/R/heatmap.R
@@ -10,13 +10,17 @@ setMethod("heatmap",
                    legend  = NULL,
                    cluster_rows = FALSE,
                    cluster_columns = FALSE,
+                   show_column_labels = FALSE,
                    clustering_distance = c("euclidean", "maximum", "manhattan", "binary", "minkowski"),
-                   clustering_method = c("average", "ward.D", "ward.D2", "single", "complete", "mcquitty", "median","centroid")) {
+                   clustering_method = c("average", "ward.D", "ward.D2", "single", "complete", "mcquitty", "median","centroid"),
+                   column_title = NULL,
+                   row_title = NULL) {
 
             # Create random env id
             env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
 
             # Input validation
+            exprs_values <- as.matrix(exprs_values)
             assertive.types::assert_is_any_of(exprs_values, c("data.frame", "matrix"))
             if(is.null(colnames(exprs_values))) colnames(exprs_values) <- paste0("V", 1:ncol(exprs_values))
 
@@ -56,6 +60,9 @@ setMethod("heatmap",
             env$cluster_columns <- cluster_columns
             env$clustering_distance <- clustering_distance
             env$clustering_method <- clustering_method
+            env$show_column_labels <- show_column_labels
+            env$column_title <- column_title
+            env$row_title <- row_title
 
             # save environment
             saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
@@ -74,7 +81,7 @@ setMethod("heatmap",
           function(dashboard,
                    object,
                    exprs_values = "counts",
-                   subset_row = NULL,
+                   subset_row,
                    split_by = NULL,
                    aggregate_by = NULL,
                    ...) {
@@ -84,7 +91,7 @@ setMethod("heatmap",
             exprs_values <- SummarizedExperiment::assay(object, exprs_values)
 
             # Subset to requested features
-            if(!is.null(subset_row)) exprs_values <- exprs_values[subset_row, ]
+            exprs_values <- exprs_values[subset_row, ]
 
             # Create data.frames for splitting and aggregation
             if(!is.null(split_by)) {
@@ -101,6 +108,53 @@ setMethod("heatmap",
                 dplyr::select(!!aggregate_by) -> aggregate_by
             }
 
+            heatmap(dashboard = dashboard,
+                    exprs_values = exprs_values,
+                    split_by = split_by,
+                    aggregate_by = aggregate_by,
+                    ...)
+          })
+
+#' @rdname heatmap
+#' @return An object of class \linkS4class{i2dash::i2dashboard}.
+#' @export
+setMethod("heatmap",
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard,
+                   object,
+                   assay = "RNA",
+                   assay_slot = "data",
+                   subset_row,
+                   split_by  = NULL,
+                   aggregate_by = NULL,
+                   ...) {
+
+            assertive.types::assert_is_character(assay)
+            assertive.types::assert_is_character(assay_slot)
+            assertive.sets::assert_is_subset(assay, names(object@assays))
+
+            # exprs_values
+            assay_obj <- Seurat::GetAssay(object = object, assay = assay)
+            exprs_values <- Seurat::GetAssayData(object = assay_obj, slot = assay_slot)
+
+            # Subset to requested features
+            exprs_values <- exprs_values[subset_row, ]
+
+            # Create data.frames for splitting and aggregation
+            if(!is.null(split_by)) {
+              assertive.sets::assert_is_subset(split_by,  colnames(object@meta.data))
+              object@meta.data %>%
+                as.data.frame() %>%
+                dplyr::select(!!split_by) -> split_by
+            }
+
+            if(!is.null(aggregate_by)) {
+              assertive.sets::assert_is_subset(aggregate_by, colnames(object@meta.data))
+              bject@meta.data %>%
+                as.data.frame() %>%
+                dplyr::select(!!aggregate_by) -> aggregate_by
+            }
+
             heatmap(dashboard,
                     exprs_values = exprs_values,
                     split_by = split_by,
diff --git a/R/multi_gene_expression_page.R b/R/multi_gene_expression_page.R
deleted file mode 100644
index 5e2f15f263e8fc2e8b1bc121472061eaaeb7ca43..0000000000000000000000000000000000000000
--- a/R/multi_gene_expression_page.R
+++ /dev/null
@@ -1,93 +0,0 @@
-#' @name feature-grid-page
-#' @rdname feature-grid-page
-#' @aliases add_feature_grid_page
-#' @return An object of class \linkS4class{i2dash::i2dashboard}.
-#' @export
-setMethod("add_feature_grid_page",
-          signature = signature(report = "i2dashboard", object = "missing"),
-          function(report, use_dimred, exprs_values, title = "Feature grid", menu = "Tools") {
-
-            # warn if no interactive mode is used
-            if(!report@interactive) warning("This page can only be used during interactive shiny sessions. Consider setting interactivity(report) <- TRUE.")
-
-            # Create random env id
-            env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
-
-            # Ensure that use_dimred is a list and has names
-            if(class(use_dimred) != "list") use_dimred <- list(use_dimred)
-
-            if(!assertive.properties::has_names(use_dimred)) {
-              names(use_dimred) <- paste0("dimred_", 1:length(use_dimred))
-            }
-
-            assertive.types::assert_is_any_of(exprs_values, c("data.frame", "matrix"))
-
-            # Create component environment
-            env <- new.env()
-            env$use_dimred <- use_dimred
-            env$exprs_values <- exprs_values
-
-            saveRDS(env, file = file.path(report@datadir, paste0(env_id, ".rds")))
-
-            # Expand component
-            timestamp <- Sys.time()
-            multi_gene_expr_component <- knitr::knit_expand(file = system.file("templates", "feature_grid.Rmd", package = "i2dash.scrnaseq"), env_id = env_id, date = timestamp)
-
-            report@pages[["gene_grid_page"]] <- list(title = title, layout = "empty", menu = menu, components = multi_gene_expr_component, max_components = 1, sidebar = NULL)
-            return(report)
-          })
-
-#' @name feature-grid-page
-#' @rdname feature-grid-page
-#' @export
-setMethod("add_feature_grid_page",
-          signature = signature(report = "i2dashboard", object = "SingleCellExperiment"),
-          function(report, object, use_dimred, exprs_values, subset_row = NULL, ...) {
-
-            assertive.sets::assert_is_subset(use_dimred, SingleCellExperiment::reducedDimNames(object))
-            assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment::assayNames(object))
-
-            if(!is.null(subset_row)) {
-              expression <- SummarizedExperiment::assay(object, i = exprs_values)[subset_row, ]
-            } else {
-              expression <- SummarizedExperiment::assay(object, i = exprs_values)
-            }
-
-            use_dimreds <- lapply(use_dimred, function(dimred) {
-              SingleCellExperiment::reducedDim(object, dimred)[, 1:2]
-            })
-
-            report <- add_gene_grid_page(report = report,
-                                         use_dimred = use_dimreds,
-                                         exprs_values = expression,
-                                         ...)
-            return(report)
-          })
-
-#' @name feature-grid-page
-#' @rdname feature-grid-page
-#' @export
-setMethod("add_feature_grid_page",
-          signature = signature(report = "i2dashboard", object = "Seurat"),
-          function(report, object, use_dimred, assay, slot = "data", subset_row = NULL) {
-
-            assertive.sets::assert_is_subset(use_dimred, names(object@reductions))
-            assertive.sets::assert_is_subset(assay, names(object@assays))
-
-            assay_obj <- Seurat::GetAssay(object = object, assay = assay)
-            expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)
-
-            if(!is.null(subset_row)) {
-              expression <- Seurat::GetAssayData(object = assay_obj, slot = slot)[subset_row, ]
-            }
-
-            use_dimreds <- lapply(use_dimred, function(dimred) {
-              Seurat::Embeddings(object, reduction = dimred)[, 1:2]
-            })
-
-            report <- add_gene_grid_page(report = report,
-                                         use_dimred = use_dimreds,
-                                         expression = expression,
-                                         ...)
-            return(report)
-          })
diff --git a/R/multiplot.R b/R/multiplot.R
deleted file mode 100644
index 38d9fe189a866a6998dee12cbbe899771af35647..0000000000000000000000000000000000000000
--- a/R/multiplot.R
+++ /dev/null
@@ -1,67 +0,0 @@
-#' Renders a Sequence saturation plot
-#'
-#' @param plot_title The title of the Component
-#' @param x A list with the x-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.
-#' @param y A list with the y-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.
-#' @param color_by A list with the color_by values. If it is a nested list, a dropdown-field will be provided in the interactive mode.
-#'
-#' @return A string containing markdown code for the rendered textbox
-multiplot <- function(object, plot_title, x, y, color_by) {
-
-  env_id <- .create_id()
-  # validate input, create environment variables, save environment object
-  .validate_input(object, env_id, x, y, color_by)
-  timestamp <- Sys.time()
-  expanded_component <- knitr::knit_expand(file = system.file("templates", "multiplot_template.Rmd", package = "i2dash"), plot_title = plot_title, env_id = env_id, date = timestamp)
-  return(expanded_component)
-}
-
-.create_id <- function(n = 1) {
-  a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE))
-  paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE))
-}
-
-.validate_input <- function(object, env_id, x, y, color_by) {
-  env <- new.env()
-  env$x_selection <- FALSE
-  env$y_selection <- FALSE
-  env$color_selection <- FALSE
-
-  # validate x and create environment variables
-  if(is.list(x) & length(x) == 1) {
-    env$x <- x
-  } else if (is.list(x) & length(x) > 1) {
-    env$x_selection <- TRUE
-    env$x <- x
-  } else if (!is.list(x) | (is.list(x) & length(x) == 0)){
-    stop("x needs to be a named list with at least one element")
-  }
-
-  # validate y and create environment variables
-  if(is.list(y) & length(y) == 1) {
-    env$y <- y
-  } else if (is.list(y) & length(y) > 1) {
-    env$y_selection <- TRUE
-    env$y <- y
-  } else if (!is.list(y) | (is.list(y) & length(y) == 0)){
-    stop("y needs to be a named list with at least one element")
-  }
-
-  # validate color_by and create environment variables
-  if(is.list(color_by) & length(color_by) == 1) {
-    env$color_by <- color_by
-  } else if (is.list(color_by) & length(color_by) > 1) {
-    env$color_selection <- TRUE
-    env$color_by <- color_by
-  } else if (!is.list(color_by) | (is.list(color_by) & length(color_by) == 0)){
-    stop("color_by needs to be a named list with at least one element")
-  }
-  # for (i in color_by){
-  #   if (!is.factor(i)){
-  #     stop("color_by needs to be a list with factorial elements")
-  #   }
-  # }
-
-  # save environment as rds-object
-  saveRDS(env, file = file.path(object@datadir, sprintf("%s.rds", env_id)))
-}
diff --git a/R/scatterplot.R b/R/scatterplot.R
index a05ab8446406929bdfea5c199c7b7cd704901c0e..8c2e88c33a5a10d6091b23f30167ac9590b66281 100644
--- a/R/scatterplot.R
+++ b/R/scatterplot.R
@@ -2,11 +2,15 @@
 #' @return A string containing markdown code for the rendered component
 setMethod("scatterplot",
           signature = signature(dashboard = "i2dashboard", object = "missing"),
-          function(dashboard, x, y, colour_by = NULL, labels = NULL, exprs_values = NULL, title = NULL, x_title = NULL, y_title = NULL) {
+          function(dashboard, x, y, colour_by = NULL, labels = NULL, exprs_values = NULL, title = NULL, x_title = NULL, y_title = NULL, plot_title = NULL) {
             # Create random env id
             env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
 
             # Validate input
+            # handle single numeric vector:
+            if(is.numeric(x)) x <- data.frame("X" <- x)
+            if(is.numeric(y)) y <- data.frame("Y" <- y)
+
             assertive.types::assert_is_any_of(x, c("data.frame", "matrix"))
             assertive.types::assert_is_any_of(y, c("data.frame", "matrix"))
             x %<>%
@@ -20,6 +24,15 @@ setMethod("scatterplot",
             if(is.null(colnames(y))) colnames(y) <- paste0("Y_", 1:ncol(y))
             if(nrow(x) != nrow(y)) stop("The number of rows in 'x' and 'y' are is equal.")
 
+            # Columns are swapped in case of equal column names to prevent visualization of the same column (always the first one) on both axes.
+            if(colnames(x)[1] == colnames(y)[1] & colnames(x)[2] == colnames(y)[2]) {
+              if(ncol(y) > 2) {
+                y <-  y[, c(2, 1, c(3:ncol(y)))]
+              } else {
+                y <-  y[, c(2, 1)]
+              }
+            }
+
             colouring <- list("No colour" = 0)
 
             if(!is.null(colour_by)){
@@ -27,6 +40,7 @@ setMethod("scatterplot",
               colour_by %<>%
                 as.data.frame() %>%
                 dplyr::select_if(function(col) is.integer(col) | is.numeric(col) | is.factor(col))
+
               if(is.null(colnames(colour_by))) colnames(colour_by) <- paste0("Colour_by_", 1:ncol(colour_by))
               if(nrow(x) != nrow(colour_by)) stop("The number of rows in 'x' and 'colour_by' is not equal.")
               colouring["Colour by metadata"] <- 1
@@ -39,7 +53,7 @@ setMethod("scatterplot",
             }
 
             if(!is.null(exprs_values)){
-              assertive.types::assert_is_any_of(exprs_values, c("data.frame", "matrix"))
+              exprs_values <- as.matrix(exprs_values)
               if(is.null(rownames(exprs_values))) rownames(exprs_values) <- paste0("feature_", 1:nrow(exprs_values))
               if(nrow(x) != ncol(exprs_values)) stop("The number of rows in 'x' and columns in 'exprs_values' is not equal.")
               colouring["Colour by expression"] <- 3
@@ -62,6 +76,7 @@ setMethod("scatterplot",
             env$colouring <- colouring
             env$x_title <- x_title
             env$y_title <- y_title
+            env$plot_title <- plot_title
 
             saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
 
@@ -77,10 +92,11 @@ setMethod("scatterplot",
 #' @export
 setMethod("scatterplot",
           signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
-          function(dashboard, object, use = "colData", x = NULL, y = NULL, colour_by = NULL, reduced_dim = NULL, ...) {
+          function(dashboard, object, use = c("colData", "rowData", "reducedDim"), x = NULL, y = NULL, colour_by = NULL, use_dimred = NULL, exprs_values = NULL, subset_row = NULL, ...) {
             #
             # use colData
             #
+            use <- match.arg(use)
             if(use == "colData") {
               labels <- rownames(SummarizedExperiment::colData(object))
               #
@@ -119,6 +135,17 @@ setMethod("scatterplot",
                 SummarizedExperiment::colData(object) %>%
                   as.data.frame() -> colour_by
               }
+              #
+              # use Assay for colouring by expression
+              #
+              if(!is.null(exprs_values)){
+                assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment::assayNames(object))
+                exprs_values <- SummarizedExperiment::assay(object, i = exprs_values)
+                if(!is.null(subset_row)) {
+                  exprs_values <- exprs_values[subset_row, ]
+                }
+              }
+
             #
             # use rowData
             #
@@ -168,10 +195,9 @@ setMethod("scatterplot",
               #
               # create data.frames for x, y
               #
-              # To Do: in statical mode the first column is used for x and y. This is useless.
-              if(!is.null(reduced_dim)) {
-                assertive.sets::assert_is_subset(reduced_dim, SingleCellExperiment::reducedDimNames(object))
-                SingleCellExperiment::reducedDim(object, reduced_dim) %>%
+              if(!is.null(use_dimred)) {
+                assertive.sets::assert_is_subset(use_dimred, SingleCellExperiment::reducedDimNames(object))
+                SingleCellExperiment::reducedDim(object, use_dimred) %>%
                   as.data.frame() -> x -> y
               } else {
                 SingleCellExperiment::reducedDim(object) %>%
@@ -189,6 +215,166 @@ setMethod("scatterplot",
                 SummarizedExperiment::colData(object) %>%
                   as.data.frame() -> colour_by
               }
+              #
+              # use Assay for colouring by expression
+              #
+              if(!is.null(exprs_values)){
+                assertive.sets::assert_is_subset(exprs_values, SummarizedExperiment::assayNames(object))
+                exprs_values <- SummarizedExperiment::assay(object, i = exprs_values)
+                if(!is.null(subset_row)) {
+                  exprs_values <- exprs_values[subset_row, ]
+                }
+              }
+            }
+
+            scatterplot(dashboard,
+                        x = x,
+                        y = y,
+                        labels = labels,
+                        colour_by = colour_by,
+                        exprs_values = exprs_values,
+                        ...)
+          })
+
+#' @rdname scatterplot
+#' @return An object of class \linkS4class{i2dash::i2dashboard}.
+#' @export
+setMethod("scatterplot",
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard, object, use = c("meta.data", "meta.features", "reduction"), x = NULL, y = NULL, colour_by = NULL, use_dimred = NULL, assay = "RNA", slot = NULL, subset_row = NULL, ...) {
+            #
+            # use meta.data
+            #
+            use <- match.arg(use)
+            if(use == "meta.data") {
+              labels <- rownames(object@meta.data)
+              #
+              # create data.frame for y
+              #
+              if(!is.null(y)) {
+                assertive.sets::assert_is_subset(y, colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!y) -> y
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> y
+              }
+              #
+              # create data.frame for x
+              #
+              if(!is.null(x)) {
+                assertive.sets::assert_is_subset(x, colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!x) -> x
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> x
+              }
+              #
+              # create data.frame for colour_by
+              #
+              if(!is.null(colour_by)) {
+                assertive.sets::assert_is_subset(colour_by, colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!colour_by) -> colour_by
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> colour_by
+              }
+              #
+              # use Assay for colouring by expression
+              #
+              if(!is.null(slot)){
+                assertive.sets::assert_is_subset(assay, names(object@assays))
+                assay_obj <- Seurat::GetAssay(object = object, assay = assay)
+                exprs_values <- Seurat::GetAssayData(object = assay_obj, slot = slot)
+                if(!is.null(subset_row)) {
+                  exprs_values <- exprs_values[subset_row, ]
+                }
+              }
+            #
+            # use meta.features
+            #
+            } else if (use == "meta.features") {
+              labels <- rownames(object[[assay]]@meta.features)
+              #
+              # create data.frame for y
+              #
+              if(!is.null(y)) {
+                assertive.sets::assert_is_subset(y, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!y) -> y
+              } else {
+                object[[assay]]@meta.features %>%
+                  as.data.frame() -> y
+              }
+              #
+              # create data.frame for x
+              #
+              if(!is.null(x)) {
+                assertive.sets::assert_is_subset(x, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!x) -> x
+              } else {
+                object[[assay]]@meta.features %>%
+                  as.data.frame() -> x
+              }
+              #
+              # create data.frame for colour_by
+              #
+              if(!is.null(colour_by)) {
+                assertive.sets::assert_is_subset(colour_by, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!colour_by) -> colour_by
+              } else {
+                object[[assay]]@meta.features %>%
+                  as.data.frame() -> colour_by
+              }
+              #
+              # use reducedDim
+              #
+            } else if (use == "reduction"){
+              assertive.sets::assert_is_subset(use_dimred, SingleCellExperiment::reducedDimNames(object))
+              labels <- rownames(Seurat::Embeddings(object, reduction = use_dimred)[, 1:2])
+              #
+              # create data.frames for x, y
+              #
+              if(!is.null(use_dimred)) {
+                Seurat::Embeddings(object, reduction = use_dimred)[, 1:2] %>%
+                  as.data.frame() -> x -> y
+              } else {
+                Seurat::Embeddings(object)[, 1:2] %>%
+                  as.data.frame() -> x -> y
+              }
+              #
+              # create data.frame for colour_by
+              #
+              if(!is.null(colour_by)) {
+                assertive.sets::assert_is_subset(colour_by, colnames(object@meta.data))
+                  object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!colour_by) -> colour_by
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> colour_by
+              }
+              #
+              # use Assay for colouring by expression
+              #
+              if(!is.null(slot)){
+                assertive.sets::assert_is_subset(assay, names(object@assays))
+                assay_obj <- Seurat::GetAssay(object = object, assay = assay)
+                exprs_values <- Seurat::GetAssayData(object = assay_obj, slot = slot)
+                if(!is.null(subset_row)) {
+                  exprs_values <- exprs_values[subset_row, ]
+                }
+              }
             }
 
             scatterplot(dashboard,
@@ -196,5 +382,6 @@ setMethod("scatterplot",
                         y = y,
                         labels = labels,
                         colour_by = colour_by,
+                        exprs_values = exprs_values,
                         ...)
           })
diff --git a/R/violinplot.R b/R/violinplot.R
index ae060cc282a81cbc57b09968137f16e03a4a17ef..75a9eeaf4d1171a09680a4dc6092e9c2c07f29bf 100644
--- a/R/violinplot.R
+++ b/R/violinplot.R
@@ -38,12 +38,11 @@ setMethod("violinplot",
           })
 
 #' @rdname violinplot
-#' @return An object of class \linkS4class{i2dash::i2dashboard}.
 #' @export
 setMethod("violinplot",
           signature = signature(dashboard = "i2dashboard", object = "SingleCellExperiment"),
-          function(dashboard, object, use = "colData", y = NULL, group_by = NULL,  title = NULL, y_title = NULL, group_by_title = NULL) {
-
+          function(dashboard, object, use = c("colData", "rowData"), y = NULL, group_by = NULL, ...) {
+            use <- match.arg(use)
             if(use == "colData") {
               if(!is.null(y)) {
                 assertive.sets::assert_is_subset(y, colnames(SummarizedExperiment::colData(object)))
@@ -80,7 +79,50 @@ setMethod("violinplot",
             violinplot(dashboard,
                        y = y,
                        group_by = group_by,
-                       title = title,
-                       y_title = y_title,
-                       group_by_title = group_by_title)
+                       ...)
+          })
+
+#' @rdname violinplot
+#' @export
+setMethod("violinplot",
+          signature = signature(dashboard = "i2dashboard", object = "Seurat"),
+          function(dashboard, object, use = c("meta.data", "meta.features"), assay = "RNA", y = NULL, group_by = NULL, ...) {
+            use <- match.arg(use)
+            if(use == "meta.data") {
+              if(!is.null(y)) {
+                assertive.sets::assert_is_subset(y, colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!y) -> y
+              } else {
+                object@meta.data %>%
+                  as.data.frame() -> y
+              }
+              if(!is.null(group_by)) {
+                assertive.sets::assert_is_subset(group_by,  colnames(object@meta.data))
+                object@meta.data %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!group_by) -> group_by
+              }
+            } else if (use == "meta.features") {
+              if(!is.null(y)) {
+                assertive.sets::assert_is_subset(y, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!y) -> y
+              } else {
+                object[[assay]]@meta.features %>%
+                  as.data.frame() -> y
+              }
+              if(!is.null(group_by)) {
+                assertive.sets::assert_is_subset(group_by, colnames(object[[assay]]@meta.features))
+                object[[assay]]@meta.features %>%
+                  as.data.frame() %>%
+                  dplyr::select(!!group_by) -> group_by
+              }
+            }
+            violinplot(dashboard,
+                    y = y,
+                    group_by = group_by,
+                    ...)
           })
diff --git a/R/visualization_functions.R b/R/visualization_functions.R
index 5943b383b4b908c1be20575fcda1046d8c99574b..aabfe85993142191f98081277e7e2ba11b2c79a7 100644
--- a/R/visualization_functions.R
+++ b/R/visualization_functions.R
@@ -1,10 +1,8 @@
 #' Creates a plotly scatterplot
 #'
-#' @param df A dataframe containing the data for the boxplot
-#' @param labels A list with sample names, which should be of the same length as x and y.
-#' @param colour_by A list containing factorial values that will be used for colouring. In case of a named list, a dropdown menu will be provided in the interactive mode. Note: The length of all vectors in case of a named list should be of the same length as x and y.
-#' @param checkbox A boolean value as indicator for colouring by labels.
-#' @param selected_label The label (character) selected by the user.
+#' @param ... these arguments are of either the form value or tag = value and should be valid for the 'plotly::plot_ly()' method.
+#' @param y_title The title of the x-axis.
+#' @param x_title The title of the y-axis.
 #'
 #' @return An object of class \code{plotly}.
 #' @export
@@ -15,9 +13,9 @@ plotly_scatterplot <- function(..., y_title = NULL, x_title = NULL){
     )
 }
 
-#' Render a bar plot with plotly.
+#' Render a barplot with plotly.
 #'
-#' @param ... these arguments are of either the form value or tag = value and should be valid for the 'plotly::plot_ly()' method.
+#' @param ... these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.
 #' @param showlegend (Optional) Boolean value that describes if the legend should be shown.
 #' @param title_x (Optional) A title that describes the observations.
 #' @param title_group_by (Optional) A title that describes the grouping factor.
@@ -25,12 +23,11 @@ plotly_scatterplot <- function(..., y_title = NULL, x_title = NULL){
 #' @return An object of class \code{plotly}.
 #' @export
 plotly_barplot <- function(..., showlegend = NULL, x_group_by_title = NULL, y_group_by_title = NULL){
-  p <- plotly::plot_ly(..., type = "bar", orientation = "h", opacity = 0.7) %>%
+  plotly::plot_ly(..., type = "bar", orientation = "h", opacity = 0.7) %>%
     plotly::layout(xaxis = list(title = x_group_by_title, showline = T),
                    yaxis = list(title = y_group_by_title, showline = T, showticklabels = T),
                    barmode = 'stack',
                    showlegend = showlegend)
-  p
 }
 
 #' Render a boxplot with plotly.
@@ -39,11 +36,12 @@ plotly_barplot <- function(..., showlegend = NULL, x_group_by_title = NULL, y_gr
 #' @param group_by A factor, by which observations can optionally be grouped.
 #' @param x_title A title that describes the observations.
 #' @param group_by_title A title that describes the grouping factor.
+#' @param ... these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.
 #'
 #' @return An object of class \code{plotly}.
 #' @export
-plotly_boxplot <- function(x, group_by = NULL, x_title = NULL, group_by_title = NULL){
-  plotly::plot_ly(x = x, y = group_by, type = "box", colors = "Set1",  color = group_by) %>%
+plotly_boxplot <- function(x, group_by = NULL, x_title = NULL, group_by_title = NULL, ...){
+  plotly::plot_ly(x = x, y = group_by, type = "box", color = group_by, ...) %>%
     plotly::layout(xaxis = list(title = x_title, showline = T),
                    yaxis = list(title = group_by_title, showline = T, showticklabels = T))
 }
@@ -54,28 +52,12 @@ plotly_boxplot <- function(x, group_by = NULL, x_title = NULL, group_by_title =
 #' @param group_by A factor, by which observations can optionally be grouped.
 #' @param y_title A title that describes the observations.
 #' @param group_by_title A title that describes the grouping factor.
+#' @param ... these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.
 #'
 #' @return An object of class \code{plotly}.
 #' @export
- plotly_violinplot <- function(y, group_by = NULL, y_title = NULL, group_by_title = NULL){
-      plotly::plot_ly(colors = "Set1", x = group_by, y = y, color = group_by, type = "violin", box = list(visible = T), meanline = list(visible = T), points = "all", jitter = 0) %>%
-      plotly::layout(
-        xaxis = list(title = group_by_title),
-        yaxis = list(title = y_title)
-      )
-}
-
-#' Render a vertical violin plot with plotly.
-#'
-#' @param y Numeric observations.
-#' @param group_by A factor, by which observations can optionally be grouped.
-#' @param y_title A title that describes the observations.
-#' @param group_by_title A title that describes the grouping factor.
-#'
-#' @return An object of class \code{plotly}.
-#' @export
- plotly_violinplot <- function(y, group_by = NULL, y_title = NULL, group_by_title = NULL){
-      plotly::plot_ly(colors = "Set1", x = group_by, y = y, color = group_by, type = "violin", box = list(visible = T), meanline = list(visible = T), points = "all", jitter = 0) %>%
+ plotly_violinplot <- function(y, group_by = NULL, y_title = NULL, group_by_title = NULL, ...){
+      plotly::plot_ly(x = group_by, y = y, color = group_by, type = "violin", box = list(visible = T), meanline = list(visible = T), points = "all", jitter = 0, ...) %>%
       plotly::layout(
         xaxis = list(title = group_by_title),
         yaxis = list(title = y_title)
@@ -84,7 +66,7 @@ plotly_boxplot <- function(x, group_by = NULL, x_title = NULL, group_by_title =
 
 #' Render a heatmap with ComplexHeatmap.
 #'
-#' @param ... further optional and valid arguments, that are the supported arguments in ComplexHeatmap.
+#' @param ... further optional arguments, that are the supported arguments in \code{ComplexHeatmap}.
 #' @param legend_title An optional title of the legend.
 #' @return An object of class \code{Heatmap}.
 #' @export
@@ -101,6 +83,23 @@ ComplexHeatmap_heatmap <- function(..., legend_title = NULL){
                             color_bar = "continuous",
                             title = legend_title
                           ))
+}
 
+#' Render a bubbleplot with plotly.
+#'
+#' @param x Numeric observations mapped to the x-axis.
+#' @param y Numeric observations mapped to the y-axis.
+#' @param size Numeric values defining the size of the dots.
+#' @param x_title The title of the x-axis.
+#' @param y_title The title of the y-axis.
+#' @param ... these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.
+#'
+#' @return An object of class \code{plotly}.
+#' @export
+plotly_bubbleplot <- function(x, y, size, x_title = NULL, y_title = NULL, ...){
+  plotly::plot_ly(x = x, y = y, size = size, type = 'scatter', mode = 'markers', marker = list( oparcity = 0.5), ...) %>%
+    plotly::layout(xaxis = list(title = x_title, showgrid = FALSE),
+                   yaxis = list(title = y_title, showgrid = FALSE)
+    )
 }
 
diff --git a/inst/templates/barplot.Rmd b/inst/templates/barplot.Rmd
index ebd0264843ccf8960bdfde6d03a7cfb03ba851d9..5477380b150aaadec565a1dd0ee5a6817952ef91 100644
--- a/inst/templates/barplot.Rmd
+++ b/inst/templates/barplot.Rmd
@@ -22,7 +22,7 @@ create_barplot_df <- function(y_group_by, x_group_by = NULL){
     df <- as.data.frame(tab)
     x <- df[2]
     y <- df[1]
-    names <- NULL
+    names <- df[1]
     showlegend <- F
     return(list("df" = df, "x" = x, "y" = y, "names" = names, "showlegend" = showlegend))
   } else {
@@ -56,19 +56,34 @@ if(!is.null({{ env_id }}$x_group_by)){
   x_group_by_{{ env_id }} <- NULL
 }
 
+  if(is.null({{ env_id }}$x_group_by)){
+    if(colnames(y_group_by_{{ env_id }}) %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[colnames(y_group_by_{{ env_id }})]] else colors_{{ env_id }} <- "Set1"
+  } else {
+    if(x_group_by_title_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[x_group_by_title_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+  }
+
 # set title variables
 if(!is.null({{ env_id }}$y_group_by_title)) y_group_by_title_{{ env_id }} <- {{ env_id }}$y_group_by_title else y_group_by_title_{{ env_id }} <- colnames(y_group_by_{{ env_id }})
 if(!is.null({{ env_id }}$x_group_by_title)) x_group_by_title_{{ env_id }} <- {{ env_id }}$x_group_by_title
 
 # Function to create a dataframe for bar plot
-output_list <- create_barplot_df(y_group_by = y_group_by_{{ env_id }}[,1], x_group_by = x_group_by_{{ env_id }}[,1])
+output_list_{{ env_id }} <- create_barplot_df(y_group_by = y_group_by_{{ env_id }}[,1], x_group_by = x_group_by_{{ env_id }}[,1])
 
 # creating the plot object
-plot_{{ env_id }} <- i2dash.scrnaseq::plotly_barplot(x = output_list$x[[1]], y = output_list$y[[1]], name = output_list$names[[1]], showlegend = output_list$showlegend, x_group_by_title = x_group_by_title_{{ env_id }}, y_group_by_title = y_group_by_title_{{ env_id }})
+plot_{{ env_id }} <-
+  i2dash.scrnaseq::plotly_barplot(
+    x = output_list_{{ env_id }}$x[[1]],
+    y = output_list_{{ env_id }}$y[[1]],
+    showlegend = output_list_{{ env_id }}$showlegend,
+    x_group_by_title = x_group_by_title_{{ env_id }},
+    y_group_by_title = y_group_by_title_{{ env_id }},
+    color = output_list_{{ env_id }}$names[[1]],
+    colors = colors_{{ env_id }}
+  )
 
 # Provide data for download
 htmltools::div(style="display:block;float:left;width:100%;height:90%;",
-  htmltools::tags$button(i2dash::embed_var(output_list$df)), plot_{{ env_id }}
+  htmltools::tags$button(i2dash::embed_var(output_list_{{ env_id }}$df)), plot_{{ env_id }}
 )
 ```
 
@@ -80,7 +95,7 @@ ui_list <- list()
 #
 if ({{ env_id }}$y_group_by_selection){
   ui_list <- rlist::list.append(ui_list,
-                                selectInput("select_y_group_by_{{ env_id }}", label = "Group x_group_by by:",
+                                selectInput("select_y_group_by_{{ env_id }}", label = "Select grouping for y axis:",
                                             choices = colnames({{ env_id }}$y_group_by[lapply({{ env_id }}$y_group_by, class) =="factor"])))
 }
 
@@ -89,7 +104,7 @@ if ({{ env_id }}$y_group_by_selection){
 #
 if ({{ env_id }}$x_group_by_selection){
   ui_list <- rlist::list.append(ui_list,
-                                selectInput("select_x_group_by_{{ env_id }}", label = "Select x_group_by:",
+                                selectInput("select_x_group_by_{{ env_id }}", label = "Select grouping for x axis:",
                                             choices = colnames({{ env_id }}$x_group_by[lapply({{ env_id }}$x_group_by, class) =="factor"])))
 }
 
@@ -137,10 +152,25 @@ output$downloadData_{{ env_id }} <- downloadHandler(
 # Reactive for plot creation
 #
 output$plot_{{ env_id }} <- plotly::renderPlotly({
+  # compare with colormaps
+  if(is.null({{ env_id }}$x_group_by)){
+    if(reactive_{{ env_id }}()$y_group_by_title %in% names(colormaps)) colors <- colormaps[[reactive_{{ env_id }}()$y_group_by_title]] else colors <- "Set1"
+  } else {
+    if(reactive_{{ env_id }}()$x_group_by_title %in% names(colormaps)) colors <- colormaps[[reactive_{{ env_id }}()$x_group_by_title]] else colors <- "Set1"
+  }
+  
   if(!is.null({{ env_id }}$y_group_by_title)) y_group_by_title <- {{ env_id }}$y_group_by_title else y_group_by_title <- reactive_{{ env_id }}()$y_group_by_title
   if(!is.null({{ env_id }}$x_group_by_title)) x_group_by_title <- {{ env_id }}$x_group_by_title else x_group_by_title <- reactive_{{ env_id }}()$x_group_by_title
 
-  i2dash.scrnaseq::plotly_barplot(x = reactive_{{ env_id }}()$barplot_input_list$x[[1]], y = reactive_{{ env_id }}()$barplot_input_list$y[[1]], name = reactive_{{ env_id }}()$barplot_input_list$names[[1]], showlegend = reactive_{{ env_id }}()$barplot_input_list$showlegend, x_group_by_title = x_group_by_title, y_group_by_title = y_group_by_title)
+  i2dash.scrnaseq::plotly_barplot(
+    x = reactive_{{ env_id }}()$barplot_input_list$x[[1]],
+    y = reactive_{{ env_id }}()$barplot_input_list$y[[1]],
+    showlegend = reactive_{{ env_id }}()$barplot_input_list$showlegend,
+    x_group_by_title = x_group_by_title,
+    y_group_by_title = y_group_by_title,
+    color = reactive_{{ env_id }}()$barplot_input_list$names[[1]],
+    colors = colors
+  )
 })
 
 #
diff --git a/inst/templates/boxplot.Rmd b/inst/templates/boxplot.Rmd
index 76c6d801409db6670d238ec7dce371d059b0d4bf..e97def8cfd8114fed663b0a6269a51ac99e25fc4 100644
--- a/inst/templates/boxplot.Rmd
+++ b/inst/templates/boxplot.Rmd
@@ -7,6 +7,8 @@
 {{ env_id }} <- readRDS(file.path(datadir, "{{ env_id }}.rds"))
 
 is_shiny <- identical(knitr::opts_knit$get("rmarkdown.runtime"), "shiny")
+
+library(magrittr)
 ```
 
 ```{r, eval=!is_shiny}
@@ -27,12 +29,19 @@ if(!is.null({{ env_id }}$group_by)){
   group_by_{{ env_id }} <- NULL
 }
 
+# compare with colormaps
+if(!is.null(group_by_title_{{ env_id }})){
+  if(group_by_title_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[group_by_title_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+} else {
+  colors_{{ env_id }} <- "Set1"
+}
+
 # set title variables
 if(!is.null({{ env_id }}$x_title)) x_title_{{ env_id }} <- {{ env_id }}$x_title else x_title_{{ env_id }} <- colnames(x_{{ env_id }})
 if(!is.null({{ env_id }}$group_by_title)) group_by_title_{{ env_id }} <- {{ env_id }}$group_by_title
 
 # creating the plot object
-plot_{{ env_id }} <- i2dash.scrnaseq::plotly_boxplot(x = x_{{ env_id }}[,1], group_by = group_by_{{ env_id }}[,1], x_title = x_title_{{ env_id }}, group_by_title = group_by_title_{{ env_id }})
+plot_{{ env_id }} <- i2dash.scrnaseq::plotly_boxplot(x = x_{{ env_id }}[,1], group_by = group_by_{{ env_id }}[,1], x_title = x_title_{{ env_id }}, group_by_title = group_by_title_{{ env_id }}, colors = colors_{{ env_id }})
 
 # Provide data for download
 if(!is.null({{ env_id }}$group_by)) download_df <- data.frame(x_{{ env_id }}, group_by_{{ env_id }}) else download_df <- data.frame(x_{{ env_id }})
@@ -86,9 +95,13 @@ if( !{{ env_id }}$x_selection){
 
 if( !{{ env_id }}$group_by_selection ) {
   group_by_{{ env_id }} <- shiny::reactive({
-    data <- {{ env_id }}$group_by[[1]]
-    title <- colnames({{ env_id }}$group_by)[1]
-    return(list(data = data, title = title))
+    if(is.null({{ env_id }}$group_by)){
+      return(list(data = NULL, title = NULL))
+    } else {
+      data <- {{ env_id }}$group_by[[1]]
+      title <- colnames({{ env_id }}$group_by)[1]
+      return(list(data = data, title = title))
+    }
   })
 } else {
   group_by_{{ env_id }} <- shiny::reactive({
@@ -104,7 +117,12 @@ if( !{{ env_id }}$group_by_selection ) {
 output$downloadData_{{ env_id }} <- downloadHandler(
   filename =  paste('data-', Sys.Date(), '.csv', sep=''),
   content = function(file) {
-    write.csv(data.frame(x_{{ env_id }}()$data, group_by_{{ env_id }}()$data), file)
+    if(is.null({{ env_id }}$group_by)){
+      df <- x_{{ env_id }}()$data
+    } else {
+      df <- data.frame(x_{{ env_id }}()$data, group_by_{{ env_id }}()$data)
+    }
+    write.csv(df, file)
   }
 )
 
@@ -112,10 +130,18 @@ output$downloadData_{{ env_id }} <- downloadHandler(
 # reactive for plot creation
 #
 output$plot_{{ env_id }} <- plotly::renderPlotly({
+  # compare with colormaps
+  if(!is.null(group_by_{{ env_id }}()$title)){
+    if(group_by_{{ env_id }}()$title %in% names(colormaps)) colors <- colormaps[[group_by_{{ env_id }}()$title]] else colors <- "Set1"
+  } else {
+    colors <- "Set1"
+  }
+  
+  # set custom axis titles if provided
   if(!is.null({{ env_id }}$x_title)) x_title <- {{ env_id }}$x_title else x_title <- x_{{ env_id }}()$title
   if(!is.null({{ env_id }}$group_by_title)) group_by_title <- {{ env_id }}$group_by_title else group_by_title <- group_by_{{ env_id }}()$title
-
-  i2dash.scrnaseq::plotly_boxplot(x = x_{{ env_id }}()$data, group_by = group_by_{{ env_id }}()$data, x_title = x_title, group_by_title = group_by_title)
+  
+  i2dash.scrnaseq::plotly_boxplot(x = x_{{ env_id }}()$data, group_by = group_by_{{ env_id }}()$data, x_title = x_title, group_by_title = group_by_title, colors = colors)
 })
 
 #
diff --git a/inst/templates/bubbleplot.Rmd b/inst/templates/bubbleplot.Rmd
new file mode 100644
index 0000000000000000000000000000000000000000..372b9a2ed71716923c7523a71cd0cf9245cc279b
--- /dev/null
+++ b/inst/templates/bubbleplot.Rmd
@@ -0,0 +1,160 @@
+
+### {{ title }}
+
+<!-- Component created on {{ date }} -->
+
+```{r}
+{{ env_id }} <- readRDS(file.path(datadir, "{{ env_id }}.rds"))
+is_shiny <- identical(knitr::opts_knit$get("rmarkdown.runtime"), "shiny")
+library(magrittr)
+```
+
+```{r, eval=!is_shiny}
+# set variables 
+# the first column is always used
+if(!is.null({{ env_id }}$x_title)) x_title_{{ env_id }} <- {{ env_id }}$x_title else x_title_{{ env_id }} <- colnames({{ env_id }}$x)[1]
+if(!is.null({{ env_id }}$y_title)) y_title_{{ env_id }} <- {{ env_id }}$y_title else y_title_{{ env_id }} <- colnames({{ env_id }}$y)[1]
+if(!is.null({{ env_id }}$colour_by)) colour_by_{{ env_id }} <- {{ env_id }}$colour_by[,1] else colour_by_{{ env_id }} <- NULL
+if(!is.null({{ env_id }}$labels)) labels_{{ env_id }} <- {{ env_id }}$labels else labels_{{ env_id }} <- rownames({{ env_id }}$x)
+
+# creating the plot object
+plot_{{ env_id }} <- i2dash.scrnaseq::plotly_bubbleplot(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], size = {{ env_id }}$size[,1], color = colour_by_{{ env_id }}, text = labels_{{ env_id }}, y_title = y_title_{{ env_id }}, x_title = x_title_{{ env_id }})
+
+# Provide data for download
+if(is.null({{ env_id }}$colour_by)){
+  df_{{ env_id }} <- data.frame(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], size = {{ env_id }}$size[,1])
+} else {
+  df_{{ env_id }} <- data.frame(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], size = {{ env_id }}$size[,1], colour_by = {{ env_id }}$colour_by[,1])
+}
+htmltools::div(style='display:block;float:left;width:100%;height:90%;',
+  htmltools::tags$button(i2dash::embed_var(df_{{ env_id }})), plot_{{ env_id }})
+```
+
+```{r, eval=is_shiny}
+#
+# shiny input widgets
+#
+ui_list <- list()
+
+# shiny input widget for x
+if (ncol({{ env_id }}$x) > 1){
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput("input_x_{{ env_id }}", label = "Select data for x axis:",
+                                            choices = colnames({{ env_id }}$x)))
+}
+
+# shiny input widget for y
+if (ncol({{ env_id }}$y) > 1){
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput("input_y_{{ env_id }}", label = "Select data for y axis:",
+                                            choices = colnames({{ env_id }}$y)))
+}
+
+# shiny input widget for size
+if (ncol({{ env_id }}$size) > 1){
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput("input_size_{{ env_id }}", label = "Select data for the size factor:",
+                                            choices = colnames({{ env_id }}$size)))
+}
+
+# shiny input widget for colour_by
+if (!is.null({{ env_id }}$colour_by)){
+  if(ncol({{ env_id }}$colour_by) > 1)
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput("input_colour_{{ env_id }}", label = "Select metadata for colouring:",
+                                            choices = colnames({{ env_id }}$colour_by)))
+}
+
+#
+# shiny download button
+#
+ui_list <- rlist::list.append(ui_list, tags$div(tags$br(), downloadButton('downloadData_{{ env_id }}', 'Download data')))
+
+#
+# Handle inputs
+#
+x_{{ env_id }} <- shiny::reactive({
+  if(ncol({{ env_id }}$x) == 1){
+    data <- {{ env_id }}$x[[1]]
+    title <- colnames({{ env_id }}$x)[1]
+    return(list(data = data, title = title))
+  } else {
+    data <- {{ env_id }}$x[[input$input_x_{{ env_id }}]]
+    title <- input$input_x_{{ env_id }}
+    return(list(data = data, title = title))
+  }
+})
+
+y_{{ env_id }} <- shiny::reactive({
+  if(ncol({{ env_id }}$y) == 1){
+    data <- {{ env_id }}$y[[1]]
+    title <- colnames({{ env_id }}$y)[1]
+    return(list(data = data, title = title))
+  } else {
+    data <- {{ env_id }}$y[[input$input_y_{{ env_id }}]]
+    title <- input$input_y_{{ env_id }}
+    return(list(data = data, title = title))
+  }
+})
+
+size_{{ env_id }} <- shiny::reactive({
+  if(ncol({{ env_id }}$size) == 1){
+    return(data <- {{ env_id }}$size[[1]])
+  } else {
+    return({{ env_id }}$size[[input$input_size_{{ env_id }}]])
+  }
+})
+
+colour_{{ env_id }} <- shiny::reactive({
+  if(!is.null({{ env_id }}$colour_by)){
+    if(ncol({{ env_id }}$colour_by) == 1){
+      return({{ env_id }}$colour_by[[1]])
+    } else {
+      return({{ env_id }}$colour_by[[input$input_colour_{{ env_id }}]])
+    }
+  } else {
+    return(NULL)
+  }
+})
+
+#
+# Download data.frame
+#
+output$downloadData_{{ env_id }} <- downloadHandler(
+  filename =  paste('data-', Sys.Date(), '.csv', sep=''),
+  content = function(file) {
+    if(is.null(colour_{{ env_id }}()$colour)){
+      df <- data.frame(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, size = size_{{ env_id }}())
+    } else {
+      df <- data.frame(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, size = size_{{ env_id }}(), colour_by = colour_{{ env_id }}()$colour)
+    }
+    write.csv(df, file)
+  }
+)
+
+#
+# reactive for plot creation
+#
+output$plot_{{ env_id }} <- plotly::renderPlotly({
+  if(!is.null({{ env_id }}$y_title)) y_title <- {{ env_id }}$y_title else y_title <- y_{{ env_id }}()$title
+  if(!is.null({{ env_id }}$x_title)) x_title <- {{ env_id }}$x_title else x_title <- x_{{ env_id }}()$title
+  if(!is.null({{ env_id }}$labels)) labels <- {{ env_id }}$labels else labels <- rownames({{ env_id }}$x)
+
+  i2dash.scrnaseq::plotly_bubbleplot(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, size = size_{{ env_id }}(), color = colour_{{ env_id }}(), text = paste0("label: ",labels, "</br>size: ",size_{{ env_id }}()), y_title = y_title, x_title = x_title)
+  
+  
+})
+
+#
+# Layout of component
+#
+shiny::fillRow(flex = c(NA, 1),
+      shinyWidgets::dropdownButton(div(style='max-height: 350px; overflow-x: auto;',do.call(shiny::inputPanel, ui_list)),
+                       circle = TRUE, status = "danger", icon = icon("gear"), width = "300px",
+                       tooltip = shinyWidgets::tooltipOptions(title = "Click, to change plot settings:"))
+      ,
+      plotly::plotlyOutput("plot_{{ env_id }}", height = "100%")
+)
+```
+
+
diff --git a/inst/templates/dimension_reduction_page/metadata_plot.Rmd b/inst/templates/dimension_reduction_page/metadata_plot.Rmd
deleted file mode 100644
index 5a5bf14a139bac15810a75bb5c43466ea8810bef..0000000000000000000000000000000000000000
--- a/inst/templates/dimension_reduction_page/metadata_plot.Rmd
+++ /dev/null
@@ -1,64 +0,0 @@
-
-###
-
-<!-- Component created on {{ date }} -->
-
-```{r, eval=!is_shiny}
-if(is.factor(df_{{ env_id }}[["{{ meta_column }}"]])){
-  bars_{{ meta_column }} <- plotly::plot_ly(sd_{{ env_id }}, colors = "Set1", x = df_{{ env_id }}[["{{ meta_column }}"]], color = df_{{ env_id }}[["{{ meta_column }}"]]) %>%
-  plotly::layout(
-    xaxis = list(title = names(df_{{ env_id }}["{{ meta_column }}"])),
-    yaxis = list(title = "Number of observations"),
-    barmode = "overlay",
-    showlegend = FALSE
-  ) %>%
-  plotly::highlight("plotly_selected")
-  bars_{{ meta_column }}
-} else {
-  boxs_{{ meta_column }} <- plotly::plot_ly(sd_{{ env_id }}, colors = "Set1", x = df_{{ env_id }}[[grouping]], y = df_{{ env_id }}[["{{ meta_column }}"]], color = df_{{ env_id }}[[grouping]]) %>%
-  plotly::add_boxplot() %>%
-  plotly::layout(
-    xaxis = list(title = names(df_{{ env_id }}["{{ meta_column }}"])),
-    yaxis = list(title = "Distribution per cluster"),
-    showlegend = FALSE
-  ) %>%
-  plotly::highlight("plotly_selected")
-  boxs_{{ meta_column }}
-}
-```
-
-```{r, eval=is_shiny}
-#
-# Output
-#
-output$plot_box_bar_{{ meta_column }}_{{ env_id }} <- plotly::renderPlotly({
-  if(is.factor(df_{{ env_id }}()$df[["{{ meta_column }}"]])){
-  bars <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = "Set1", x = df_{{ env_id }}()$df[["{{ meta_column }}"]], color = df_{{ env_id }}()$df[["{{ meta_column }}"]]) %>%
-  plotly::layout(
-    xaxis = list(title = names(df_{{ env_id }}()$df["{{ meta_column }}"])),
-    yaxis = list(title = "Number of observations"),
-    barmode = "overlay",
-    showlegend = FALSE
-  ) %>%
-  plotly::highlight("plotly_selected")
-  bars
-} else {
-  boxs <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = "Set1", x = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], y = df_{{ env_id }}()$df[["{{ meta_column }}"]], color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]]) %>%
-  plotly::add_boxplot() %>%
-  plotly::layout(
-    xaxis = list(title = names(df_{{ env_id }}()$df["{{ meta_column }}"])),
-    yaxis = list(title = "Distribution per cluster"),
-    showlegend = FALSE
-  ) %>%
-  plotly::highlight("plotly_selected")
-  boxs
-}
-})
-
-#
-# Layout of component
-#
-plotly::plotlyOutput("plot_box_bar_{{ meta_column }}_{{ env_id }}", height = "100%")
-```
-
-
diff --git a/inst/templates/dimred_metadata.Rmd b/inst/templates/dimred_metadata.Rmd
new file mode 100644
index 0000000000000000000000000000000000000000..a1c178465e2eb88e3fa63df22a200d7501541053
--- /dev/null
+++ b/inst/templates/dimred_metadata.Rmd
@@ -0,0 +1,177 @@
+    
+### 
+
+<!-- Component created on 2019-08-08 15:09:49 -->
+
+```{r}
+{{ env_id }} = readRDS(file.path(datadir, "{{ env_id }}.rds"))
+
+is_shiny <- identical(knitr::opts_knit$get("rmarkdown.runtime"), "shiny")
+
+library(magrittr)
+
+reduced_dim_{{ env_id }} <- {{ env_id }}$reduced_dim
+expression_{{ env_id }} <- {{ env_id }}$expression
+metadata_{{ env_id }} <- {{ env_id }}$metadata
+featurenames <- rownames(expression_{{ env_id }})
+expr_str_{{ env_id }} <- apply(expression_{{ env_id }}, 1, paste0, collapse = ";")
+names(expr_str_{{ env_id }}) <- rownames(expression_{{ env_id }})
+```
+
+```{r, eval=!is_shiny, results='asis'}
+#
+# Store expression data in div containers
+#
+data_divs <- lapply(featurenames, function(feature) {
+  e <- expr_str_{{ env_id }}[[feature]]
+  htmltools::div(id = paste0("expression-", feature), `data-feature` = feature, `data-expression` = e)
+})
+htmltools::div(data_divs)
+```
+
+
+```{js, eval=!is_shiny}
+/**
+ * Function to link a div-container with a plotly scatterplot (needs to have only two traces), change the color values and the title of the plot.
+ * @param {string} plot_id The id of the scatterplot that should be changed.
+ * @param {string} div_id The id of the div container.
+ * @param {string} color_by_tag The tag of the div container containing the values for the colorchange (currently as a semicolon separated string).
+ * @param {string} title The new title of the scatterplot.
+ */
+function linking_plotly_scatter(plot_id, div_id, color_by_tag, title = "") {
+
+  color_by = document.getElementById(div_id).getAttribute(color_by_tag);
+
+  // (decode the data) or split the datastring
+  var color_by = color_by.split(';').map(function(item){return parseFloat(item);});
+
+  // get min and max values from expression
+  var min = Math.min(...color_by);
+  var max = Math.max(...color_by);
+  
+  // update scatterplot data
+  var scatter_update_0 = {
+  'marker.color': [color_by],
+  'marker.cmax': max,
+  'marker.cmin': min,
+  'marker.line.cmax': max,
+  'marker.line.cmin': min,
+  'marker.line.color': [color_by]};
+
+  var scatter_update_1 = {
+  'marker.color': [min, max],
+  'marker.cmax': max,
+  'marker.cmin': min};
+  // update scatterplot layout
+  var scatter_layout_update = {title: title};
+  
+  // restyle and relayout of the scatterplot
+  Plotly.restyle(plot_id, scatter_update_0, [0]);
+  Plotly.restyle(plot_id, scatter_update_1, [1]);
+  Plotly.relayout(plot_id, scatter_layout_update);
+}
+```
+
+```{r, eval=!is_shiny}
+default_expression <- expression_{{ env_id }}[1, ]
+i2dash.scrnaseq::plotly_scatterplot(x = reduced_dim_{{ env_id }}$x, y = reduced_dim_{{ env_id }}$y, color = default_expression, colors = "YlOrRd", text = row.names(reduced_dim_{{ env_id }}), hoverinfo = "x+y+text", y_title = "Dimension 2", x_title = "Dimension 1") %>%
+  plotly::layout(
+    title = featurenames[1]
+  ) -> plot_{{ env_id }}
+plot_{{ env_id }}$elementId <- "plot_{{ env_id }}"
+
+htmltools::div(style="display:block;float:left;width:100%;height:90%;padding-left:15px", htmltools::HTML("<p>Please select a feature in the left table to show its expression values.</p>"),plot_{{ env_id }})
+```
+
+```{r, eval=is_shiny}
+ui_list <- list()
+
+#
+# Create reactive data table
+#
+df_{{ env_id }} <- shiny::reactive({
+  selected_gene <- input$tbl_{{ env_id }}_row_last_clicked
+  if(is.null(selected_gene)){
+    expression <- expression_{{ env_id }}[1,] 
+    featurename <- featurenames[1]
+  } else {
+    expression <- expression_{{ env_id }}[selected_gene,]
+    featurename <- featurenames[selected_gene]
+  }
+
+  df <- data.frame("x" = reduced_dim_{{ env_id }}[,1], "y" = reduced_dim_{{ env_id }}[,2], "label" = row.names(reduced_dim_{{ env_id }}), "expression" = expression)
+  
+  return(list("df" = df, "feature" = featurename))
+})
+
+# Download link
+ui_list <- rlist::list.append(ui_list, tags$div(tags$br(), shiny::downloadButton('downloadData_{{ env_id }}', 'Download data')))
+
+#
+# Download
+#
+output$downloadData_{{ env_id }}<- shiny::downloadHandler(
+  filename =  paste('data-', Sys.Date(), '.csv', sep=''),
+  content = function(file) {
+    write.csv(df_{{ env_id }}()$df, file)
+  }
+)
+
+#
+# Output
+#
+output$plot_scatter_{{ env_id }} <- plotly::renderPlotly({
+  i2dash.scrnaseq::plotly_scatterplot(x = df_{{ env_id }}()$df$x, y = df_{{ env_id }}()$df$y, color = df_{{ env_id }}()$df$expression, colors = "YlOrRd", text = df_{{ env_id }}()$df$label, hoverinfo = "x+y+text", y_title = "Dimension 2", x_title = "Dimension 1") %>%
+  plotly::layout(
+    title = df_{{ env_id }}()$feature
+  ) -> scatter
+  scatter
+})
+
+#
+# Layout of component
+#
+shiny::fillRow(flex = c(NA, 1),
+      shinyWidgets::dropdownButton(div(style='max-height: 350px; overflow-x: auto;',do.call(shiny::inputPanel, ui_list)),
+                       circle = TRUE, status = "danger", icon = icon("gear"), width = "300px",
+                       tooltip = shinyWidgets::tooltipOptions(title = "Click, to change plot settings:")),
+      htmltools::div(style="display:block;float:left;width:100%;height:85%;padding-left:15px", htmltools::HTML("<p>Please select a feature in the left table to show its expression values.</p>"), plotly::plotlyOutput("plot_scatter_{{ env_id }}", height = "100%"))
+)
+```
+
+Column
+----------------------------------------------------
+
+###
+
+```{r, eval=!is_shiny}
+cbind(metadata_{{ env_id }}) %>%
+  DT::datatable(filter = 'top', selection = list(mode = 'single', target = 'row'),
+    callback = DT::JS("table.on('click.dt', 'tr', function() {
+
+    var id_scatter = 'plot_{{ env_id }}',
+    feature_name = table.row(this).data()[0],
+    feature_div_id = 'expression-'.concat(feature_name);
+
+    linking_plotly_scatter(plot_id = id_scatter, div_id = feature_div_id, color_by_tag = 'data-expression', title = feature_name);
+    
+    // highlight selected row of the table
+    if ( $(this).hasClass('selected') ) {
+        $(this).removeClass('selected');
+    }
+    else {
+        table.$('tr.selected').removeClass('selected');
+        $(this).addClass('selected');
+    }
+    })"))
+```
+
+```{r, eval=is_shiny}
+output$tbl_{{ env_id }} <- DT::renderDataTable({
+  options(DT.options = list(scrollY="400px",scrollX="300px", pageLength = 50, autoWidth = TRUE))
+  DT::datatable(metadata_{{ env_id }}, filter = 'top', selection = list(mode = 'single', selected = 1, target = 'row'))
+})
+
+DT::dataTableOutput('tbl_{{ env_id }}')
+```
+
diff --git a/inst/templates/dimred_metadata_table.Rmd b/inst/templates/dimred_metadata_table.Rmd
deleted file mode 100644
index 4f06db0680bb054f9bee290df2af360b6dfee66c..0000000000000000000000000000000000000000
--- a/inst/templates/dimred_metadata_table.Rmd
+++ /dev/null
@@ -1,63 +0,0 @@
-###
-
-```{r, eval=!is_shiny}
-DT::datatable(df_couns_stats_{{ env_id }}, filter = 'top', selection = list(mode = 'single', target = 'row'), 
-              options = list(
-    columnDefs = list(list(visible = FALSE, targets = c(1,2)))),
-    callback=DT::JS("
-  table.on('click.dt', 'tr', function() {
-  var id = 'plot_{{ env_id }}';
-  var data = table.row(this).data()[1];
-  var featurename = table.row(this).data()[2];
-  var gd = document.getElementById(id)
-  var layout_update = {title: featurename};
-  var expr = data.split(';').map(function(item){return parseFloat(item);});
-  var min = Math.min(...expr);
-  var max = Math.max(...expr);
-  
-  var data_update_0 = {
-  'marker.color': [expr],
-  'marker.cmax': max,
-  'marker.cmin': min,
-  'marker.line.cmin': min,
-  'marker.line.cmax': max,
-  'marker.line.color': [expr]};
-
-  var data_update_1 = {
-  'marker.color': [min, max],
-  'marker.cmax': max,
-  'marker.cmin': min};
-  
-  Plotly.restyle(id, data_update_0, [0]);
-  Plotly.restyle(id, data_update_1, [1]);
-  Plotly.relayout(id, layout_update);
-  
-  if ( $(this).hasClass('selected') ) {
-      $(this).removeClass('selected');
-  }
-  else {
-      table.$('tr.selected').removeClass('selected');
-      $(this).addClass('selected');
-  }
-  
-  /* console.log(featurename);
-  console.log(expr);
-  console.log(min);
-  console.log(max);
-  console.log(gd.data);
-  console.log(gd.layout); */
-});
-")
-)
-```
-
-```{r, eval=is_shiny}
-output$tbl_{{ env_id }} <- DT::renderDataTable({
-  options(DT.options = list(scrollY="400px",scrollX="300px", pageLength = 50, autoWidth = TRUE))
-  DT::datatable(metadata_{{ env_id }}, filter = 'top', selection = list(mode = 'single', selected = 1, target = 'row'))
-})
-
-DT::dataTableOutput('tbl_{{ env_id }}')
-```
-
-
diff --git a/inst/templates/dimension_reduction_page/barplot_group_sizes.Rmd b/inst/templates/dimred_sample_page/barplot_group_sizes.Rmd
similarity index 55%
rename from inst/templates/dimension_reduction_page/barplot_group_sizes.Rmd
rename to inst/templates/dimred_sample_page/barplot_group_sizes.Rmd
index 45b47c7e8e71eb4bec102d482a1d7022cf680737..3dfd4c712e6bb43d2918462de40bcd6d740f1a28 100644
--- a/inst/templates/dimension_reduction_page/barplot_group_sizes.Rmd
+++ b/inst/templates/dimred_sample_page/barplot_group_sizes.Rmd
@@ -4,9 +4,10 @@
 <!-- Component created on {{ date }} -->
 
 ```{r, eval=!is_shiny}
-sp_bars <- plotly::plot_ly(sd_{{ env_id }}, colors = "Set1", x = df_{{ env_id }}[[grouping]], color = df_{{ env_id }}[[grouping]]) %>%
+if(grouping_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[grouping_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+sp_bars <- plotly::plot_ly(sd_{{ env_id }}, colors = colors_{{ env_id }}, x = df_{{ env_id }}[[grouping_{{ env_id }}]], color = df_{{ env_id }}[[grouping_{{ env_id }}]]) %>%
   plotly::layout(
-    xaxis = list(title = names(df_{{ env_id }}[grouping])),
+    xaxis = list(title = names(df_{{ env_id }}[grouping_{{ env_id }}])),
     yaxis = list(title = "Number of observations"),
     barmode = "overlay",
     showlegend = FALSE
@@ -20,7 +21,8 @@ sp_bars
 # Output
 #
 output$plot_sp_bars_{{ env_id }} <- plotly::renderPlotly({
-  sp_bars <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = "Set1", x = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]]) %>%
+   if(df_{{ env_id }}()$grouping %in% names(colormaps)) colors <- colormaps[[df_{{ env_id }}()$grouping]] else colors <- "Set1"
+  sp_bars <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = colors, x = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]]) %>%
   plotly::layout(
     xaxis = list(title = names(df_{{ env_id }}()$df[df_{{ env_id }}()$grouping])),
     yaxis = list(title = "Number of observations"),
diff --git a/inst/templates/dimred_sample_page/metadata_plot.Rmd b/inst/templates/dimred_sample_page/metadata_plot.Rmd
new file mode 100644
index 0000000000000000000000000000000000000000..aa305ed1d188358561631b94103b3427abfbc1dd
--- /dev/null
+++ b/inst/templates/dimred_sample_page/metadata_plot.Rmd
@@ -0,0 +1,68 @@
+
+###
+
+<!-- Component created on {{ date }} -->
+
+```{r, eval=!is_shiny}
+if(is.factor(df_{{ env_id }}[["{{ meta_column }}"]])){
+  if("{{ meta_column }}" %in% names(colormaps)) colors_{{ env_id }} <- colormaps[["{{ meta_column }}"]] else colors_{{ env_id }} <- "Set1"
+  bars_{{ meta_column }} <- plotly::plot_ly(sd_{{ env_id }}, colors = colors_{{ env_id }}, x = df_{{ env_id }}[["{{ meta_column }}"]], color = df_{{ env_id }}[["{{ meta_column }}"]]) %>%
+  plotly::layout(
+    xaxis = list(title = names(df_{{ env_id }}["{{ meta_column }}"])),
+    yaxis = list(title = "Number of observations"),
+    barmode = "overlay",
+    showlegend = FALSE
+  ) %>%
+  plotly::highlight("plotly_selected")
+  bars_{{ meta_column }}
+} else {
+  if(grouping_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[grouping_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+  box_{{ meta_column }} <- plotly::plot_ly(sd_{{ env_id }}, colors = colors_{{ env_id }}, x = df_{{ env_id }}[[grouping_{{ env_id }}]], y = df_{{ env_id }}[["{{ meta_column }}"]], color = df_{{ env_id }}[[grouping_{{ env_id }}]]) %>%
+  plotly::add_boxplot() %>%
+  plotly::layout(
+    xaxis = list(title = grouping_{{ env_id }}),
+    yaxis = list(title = names(df_{{ env_id }}["{{ meta_column }}"])),
+    showlegend = FALSE
+  ) %>%
+  plotly::highlight("plotly_selected")
+  box_{{ meta_column }}
+}
+```
+
+```{r, eval=is_shiny}
+#
+# Output
+#
+output$plot_box_bar_{{ meta_column }}_{{ env_id }} <- plotly::renderPlotly({
+  if(is.factor(df_{{ env_id }}()$df[["{{ meta_column }}"]])){
+    if("{{ meta_column }}" %in% names(colormaps)) colors <- colormaps[["{{ meta_column }}"]] else colors <- "Set1"
+    bars <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = colors, x = df_{{ env_id }}()$df[["{{ meta_column }}"]], color = df_{{ env_id }}()$df[["{{ meta_column }}"]]) %>%
+    plotly::layout(
+      xaxis = list(title = names(df_{{ env_id }}()$df["{{ meta_column }}"])),
+      yaxis = list(title = "Number of observations"),
+      barmode = "overlay",
+      showlegend = FALSE
+    ) %>%
+    plotly::highlight("plotly_selected")
+    bars
+  } else {
+    if(df_{{ env_id }}()$grouping %in% names(colormaps)) colors <- colormaps[[df_{{ env_id }}()$grouping]] else colors <- "Set1"
+    box <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = colors, x = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], y = df_{{ env_id }}()$df[["{{ meta_column }}"]], color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]]) %>%
+    plotly::add_boxplot() %>%
+    plotly::layout(
+      xaxis = list(title = df_{{ env_id }}()$grouping),
+      yaxis = list(title = names(df_{{ env_id }}()$df["{{ meta_column }}"])),
+      showlegend = FALSE
+    ) %>%
+    plotly::highlight("plotly_selected")
+    box
+  }
+})
+
+#
+# Layout of component
+#
+plotly::plotlyOutput("plot_box_bar_{{ meta_column }}_{{ env_id }}", height = "100%")
+```
+
+
diff --git a/inst/templates/dimension_reduction_page/scatterplot_dimred.Rmd b/inst/templates/dimred_sample_page/scatterplot_dimred.Rmd
similarity index 63%
rename from inst/templates/dimension_reduction_page/scatterplot_dimred.Rmd
rename to inst/templates/dimred_sample_page/scatterplot_dimred.Rmd
index 472bb579993cc8eff2b4775c2eba68e5db2d8704..12d1ec84cfd2cc858d4a6378d86d9a088b86d3ef 100644
--- a/inst/templates/dimension_reduction_page/scatterplot_dimred.Rmd
+++ b/inst/templates/dimred_sample_page/scatterplot_dimred.Rmd
@@ -11,9 +11,12 @@ is_shiny <- identical(knitr::opts_knit$get("rmarkdown.runtime"), "shiny")
 ########
 library(dplyr)
 ########
+labels_{{ env_id }} <- {{ env_id }}$labels
+metadata_{{ env_id }} <- {{ env_id }}$metadata
+grouping_{{ env_id }} <- {{ env_id }}$grouping
 
 # function to create a data.frame
-create_df <- function(reduced_dim, metadata, grouping){
+create_df_{{ env_id }} <- function(reduced_dim, metadata, grouping){
   
   # Create data.frame for linked plots
   # 1 Create df1 
@@ -39,17 +42,17 @@ create_df <- function(reduced_dim, metadata, grouping){
 ```
 
 ```{r, eval=!is_shiny}
-grouping <- {{ env_id }}$grouping
-labels <- {{ env_id }}$labels
-
-df_{{ env_id }} <- create_df(reduced_dim = {{ env_id }}$reduced_dim, metadata = {{ env_id }}$metadata, grouping = {{ env_id }}$grouping)
+df_{{ env_id }} <- create_df_{{ env_id }}(reduced_dim = {{ env_id }}$reduced_dim, metadata = metadata_{{ env_id }}, grouping = grouping_{{ env_id }})
 
 sd_{{ env_id }} <- plotly::highlight_key(df_{{ env_id }})
 
-dots <- plotly::plot_ly(sd_{{ env_id }}, colors = "Set1", color = df_{{ env_id }}[[grouping]], x = ~x, y = ~y, mode = "markers", textposition = "top", hoverinfo = "x+y+text", text = paste("grouping: ", df_{{ env_id }}[[grouping]], "</br> label: ", labels)) %>%
+# compare with colormaps
+if(grouping_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[grouping_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+
+dots <- plotly::plot_ly(sd_{{ env_id }}, colors = colors_{{ env_id }}, color = df_{{ env_id }}[[grouping_{{ env_id }}]], x = ~x, y = ~y, mode = "markers", textposition = "top", hoverinfo = "x+y+text", text = paste("grouping: ", df_{{ env_id }}[[grouping_{{ env_id }}]], "</br> label: ", labels_{{ env_id }}), type = "scattergl") %>%
   plotly::layout(
-    xaxis = list(title = "Dim 1"),
-    yaxis = list(title = "Dim 2"),
+    xaxis = list(title = "Dimension 1"),
+    yaxis = list(title = "Dimension 2"),
     showlegend = FALSE
   ) %>%
   plotly::highlight("plotly_selected")
@@ -59,14 +62,10 @@ htmltools::div(style="display:block;float:left;width:100%;height:90%;",
 ```
 
 ```{r, eval=is_shiny}
-labels <- {{ env_id }}$labels
-metadata <- {{ env_id }}$metadata
-grouping <- {{ env_id }}$grouping
-
 # filter all factorial metadata columns
 factors <- c()
-for (name in colnames(metadata)){
-  if(is.factor(metadata[[name]])){
+for (name in colnames(metadata_{{ env_id }})){
+  if(is.factor(metadata_{{ env_id }}[[name]])){
     factors <- c(factors, name)
   }
 }
@@ -76,7 +75,7 @@ ui_list <- list()
 if ({{ env_id }}$multiple_meta){
   ui_list <- rlist::list.append(ui_list,
                                 selectInput("select_grouping_{{ env_id }}", label = "Select column for grouping:",
-                                            choices = factors, selected = grouping))
+                                            choices = factors, selected = grouping_{{ env_id }}))
 }
 
 
@@ -89,11 +88,11 @@ ui_list <- rlist::list.append(ui_list, tags$div(tags$br(), downloadButton('downl
 #
 df_{{ env_id }} <- shiny::reactive({
   if (!{{ env_id }}$multiple_meta){
-    grouping <- {{ env_id }}$grouping
+    grouping <- grouping_{{ env_id }}
   } else {
     grouping <- input$select_grouping_{{ env_id }}
   }
-  df_{{ env_id }} <- create_df(reduced_dim = {{ env_id }}$reduced_dim, metadata = {{ env_id }}$metadata, grouping = grouping)
+  df_{{ env_id }} <- create_df_{{ env_id }}(reduced_dim = {{ env_id }}$reduced_dim, metadata = metadata_{{ env_id }}, grouping = grouping)
 
   sd_{{ env_id }} <- plotly::highlight_key(df_{{ env_id }})
   return(list("sd" = sd_{{ env_id }}, "df" = df_{{ env_id }}, "grouping" = grouping))
@@ -113,10 +112,13 @@ output$downloadData_{{ env_id }}<- downloadHandler(
 # Output
 #
 output$plot_scatter_{{ env_id }} <- plotly::renderPlotly({
-  dots <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = "Set1", color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], x = ~x, y = ~y, mode = "markers", textposition = "top", hoverinfo = "x+y+text", text = paste("grouping: ", df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], "</br> label: ", labels)) %>%
+  # compare with colormaps
+  if(df_{{ env_id }}()$grouping %in% names(colormaps)) colors <- colormaps[[df_{{ env_id }}()$grouping]] else colors <- "Set1"
+  
+  dots <- plotly::plot_ly(df_{{ env_id }}()$sd, color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], x = ~x, y = ~y, mode = "markers", textposition = "top", hoverinfo = "x+y+text", text = paste("grouping: ", df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], "</br> label: ", labels_{{ env_id }}),  type = "scattergl", colors = colors) %>%
   plotly::layout(
-    xaxis = list(title = "Dim 1"),
-    yaxis = list(title = "Dim 2"),
+    xaxis = list(title = "Dimension 1"),
+    yaxis = list(title = "Dimension 2"),
     showlegend = FALSE
   ) %>%
   plotly::highlight("plotly_selected")
diff --git a/inst/templates/dimension_reduction_page/silhouette_plot.Rmd b/inst/templates/dimred_sample_page/silhouette_plot.Rmd
similarity index 52%
rename from inst/templates/dimension_reduction_page/silhouette_plot.Rmd
rename to inst/templates/dimred_sample_page/silhouette_plot.Rmd
index 63b21da28a5584cc34f9704ffe53ad90f72adbcc..71542ec0d41bf445ea1fa8161aec9e08478aceeb 100644
--- a/inst/templates/dimension_reduction_page/silhouette_plot.Rmd
+++ b/inst/templates/dimred_sample_page/silhouette_plot.Rmd
@@ -4,8 +4,9 @@
 <!-- Component created on {{ date }} -->
 
 ```{r, eval=!is_shiny}
-sil_plot <- plotly::plot_ly(sd_{{ env_id }}, colors = "Set1") %>%
-  plotly::add_bars(color = df_{{ env_id }}[[grouping]], name = df_{{ env_id }}[[grouping]], orientation = 'h', mode = "bar", y = ~cell, x = ~silhouette) %>%
+if(grouping_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[grouping_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+sil_plot <- plotly::plot_ly(sd_{{ env_id }}, colors = colors_{{ env_id }}) %>%
+  plotly::add_bars(color = df_{{ env_id }}[[grouping_{{ env_id }}]], name = df_{{ env_id }}[[grouping_{{ env_id }}]], orientation = 'h', y = ~cell, x = ~silhouette) %>%
   plotly::layout(yaxis = list(showticklabels = FALSE), showlegend = FALSE) %>%
   plotly::highlight(on = "plotly_selected", dynamic = F, selectize = F)
 sil_plot
@@ -16,8 +17,9 @@ sil_plot
 # Output
 #
 output$plot_sil_{{ env_id }} <- plotly::renderPlotly({
-  sil_plot <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = "Set1") %>%
-  plotly::add_bars(color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], name = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], orientation = 'h', mode = "bar", y = ~cell, x = ~silhouette) %>%
+  if(df_{{ env_id }}()$grouping %in% names(colormaps)) colors <- colormaps[[df_{{ env_id }}()$grouping]] else colors <- "Set1"
+  sil_plot <- plotly::plot_ly(df_{{ env_id }}()$sd, colors = colors) %>%
+  plotly::add_bars(color = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], name = df_{{ env_id }}()$df[[df_{{ env_id }}()$grouping]], orientation = 'h', y = ~cell, x = ~silhouette) %>%
   plotly::layout(yaxis = list(showticklabels = FALSE), showlegend = FALSE) %>%
   plotly::highlight(on = "plotly_selected", dynamic = F, selectize = F)
   sil_plot
@@ -28,3 +30,5 @@ output$plot_sil_{{ env_id }} <- plotly::renderPlotly({
 #
 plotly::plotlyOutput("plot_sil_{{ env_id }}", height = "100%")
 ```
+
+
diff --git a/inst/templates/feature_grid.Rmd b/inst/templates/feature_grid.Rmd
index 410d14e2d2fb57468c34dbcffd82e29a877c48cc..1b14a41c1a7fe3d7407672b708afc53520bf898f 100644
--- a/inst/templates/feature_grid.Rmd
+++ b/inst/templates/feature_grid.Rmd
@@ -15,7 +15,7 @@ exprs_values_{{ env_id }} <- {{ env_id }}$exprs_values
 #
 create_feature_figure <- function(feature, data, mapping = viridis::scale_color_viridis()){
   data %>%
-    ggplot2::ggplot(ggplot2::aes_string(x = "dim1", y = "dim2", color = feature)) +
+    ggplot2::ggplot(ggplot2::aes_string(x = "dim1", y = "dim2", color = make.names(feature))) +
     ggplot2::geom_point() +
     mapping +
     ggplot2::labs(color = feature, x = "", y = "", title = "") +
diff --git a/inst/templates/features_by_factors_template.Rmd b/inst/templates/features_by_factors_template.Rmd
deleted file mode 100644
index f716941790afd33166703ffd30e6fa0006c0f359..0000000000000000000000000000000000000000
--- a/inst/templates/features_by_factors_template.Rmd
+++ /dev/null
@@ -1,106 +0,0 @@
-
-### {{ title }}
-
-<!-- Component created on {{ date }} -->
-
-```{r}
-{{ env_id }} = readRDS(file.path(datadir, "{{ env_id }}.rds"))
-
-is_shiny <- identical(knitr::opts_knit$get("rmarkdown.runtime"), "shiny")
-```
-
-```{r, eval=!is_shiny}
-
-x_value <- {{ env_id }}$x[[1]]
-x_title <- names({{ env_id }}$x)
-
-y_value <- {{ env_id }}$y[[1]]
-y_title <- names({{ env_id }}$y)
-
-p <- plotly::plot_ly(data.frame(x_value, y_value),
-             x = x_value,
-             y = y_value,
-             split = x_value,
-             type = 'violin',
-             box = list(
-               visible = T
-               ),
-             meanline = list(
-               visible = T
-               )
-)
-p <- plotly::layout(p,
-            xaxis = list(title = x_title),
-            yaxis = list(title = y_title,
-                        zeroline = F))
-p
-
-```
-
-```{r, eval=is_shiny}
-
-ui_list <- list()
-# selection field for x
-if ({{ env_id }}$x_selection){
-  ui_list <- rlist::list.append(ui_list,
-  selectInput("select_x_{{ env_id }}", label = "Select data for x axis:",
-          choices = names({{ env_id }}$x)))
-}
-
-# selection field for y
-if ({{ env_id }}$y_selection){
-ui_list <- rlist::list.append(ui_list,
-selectInput("select_y_{{ env_id }}", label = "Select data for y axis:",
-          choices = names({{ env_id }}$y)))
-}
-
-
-fillCol(flex = c(NA, 1),
-        do.call("inputPanel", ui_list),
-        plotly::plotlyOutput("plot_{{ env_id }}", height = "100%"))
-
-output$plot_{{ env_id }} <- plotly::renderPlotly({
-
-  if (!{{ env_id }}$x_selection){
-    x_value <- {{ env_id }}$x[[1]]
-    x_title <- names({{ env_id }}$x)
-  } else {
-    x_value <- {{ env_id }}$x[[input$select_x_{{ env_id }}]]
-    x_title <- input$select_x_{{ env_id }}
-  }
-
-  if (!{{ env_id }}$y_selection){
-    y_value <- {{ env_id }}$y[[1]]
-    y_title <- names({{ env_id }}$y)
-  } else {
-    y_value <- {{ env_id }}$y[[input$select_y_{{ env_id }}]]
-    y_title <- input$select_y_{{ env_id }}
-  }
-
-  p <- plotly::plot_ly(data.frame(x_value, y_value),
-               x = x_value,
-               y = y_value,
-               split = x_value,
-               type = 'violin',
-               box = list(
-                 visible = T
-                 ),
-               meanline = list(
-                 visible = T
-                 )
-  )
-  p <- plotly::layout(p,
-                    xaxis = list(title = x_title),
-                    yaxis = list(title = y_title,
-                                 zeroline = F))
-  p
-})
-```
-
-***
-
-Plot description:
-
-Sequencing is called *saturated* when generating more sequencing output from a cDNA library does not substantially increase the number of detected features in a sample. Since the number of detected features can act as a technical confounder, and thereby drive substructure in the data, it is advisable to aim for a saturated sequencing by either adding more sequencing output or decreasing the number of samples until saturation is achieved. [@zhang_one_2018] gives advise on how to choose the optimal cell number given a fixed sequencing budget
-
-
diff --git a/inst/templates/heatmap.Rmd b/inst/templates/heatmap.Rmd
index 2ab88b40ba77e39467dc374cc4e858d1b33bab40..4654f858f576944dc20b6a422ba9e548eef9734f 100644
--- a/inst/templates/heatmap.Rmd
+++ b/inst/templates/heatmap.Rmd
@@ -25,25 +25,40 @@ aggregate_data <- function(data, group) {
 exprs_values_{{ env_id }} <- {{ env_id }}$exprs_values
 split_by_{{ env_id }} <- NULL
 cell_fun_{{ env_id }} <- NULL
-column_names_rot_{{ env_id }} <- NULL
+column_names_rot_{{ env_id }} <- 45
+ha_{{ env_id }} <- NULL
 
 # Handle column split
 if (!is.null({{ env_id }}$split_by)) {
   split_by_{{ env_id }} <- {{ env_id }}$split_by[,1]
+  
+  # Handle annotation
+  # Use colors defined by colormap else use random colors
+  if (colnames({{ env_id }}$split_by)[1] %in% names(colormaps)){
+    ha_{{ env_id }} = ComplexHeatmap::HeatmapAnnotation(
+      group_by = split_by_{{ env_id }},
+      col = list(group_by = colormaps[[colnames({{ env_id }}$split_by)[1]]])
+    )
+  } else {
+    ha_{{ env_id }} = ComplexHeatmap::HeatmapAnnotation(
+      group_by = split_by_{{ env_id }}
+    )
+  }
+  
 }
 
+# Remove column names
+if (!{{ env_id }}$show_column_labels) colnames(exprs_values_{{ env_id }}) <- c()
+{{ env_id }}$aggregate_by <- NULL
 # Handle data aggregation
 if (!is.null({{ env_id }}$aggregate_by)) {
   exprs_values_{{ env_id }} <- aggregate_data({{ env_id }}$exprs_values, {{ env_id }}$aggregate_by[,1])
-  column_names_rot_{{ env_id }} <- 45
   cell_fun_{{ env_id }} <- function(j, i, x, y, width, height, fill) {
       grid::grid.text(sprintf("%.1f", exprs_values_{{ env_id }}[i, j]), x, y, gp = grid::gpar(fontsize = 10))
-    }
+  }
+  split_by_{{ env_id }} <- NULL
 }
 
-# Remove column names
-colnames(exprs_values_{{ env_id }}) <- c()
-
 # Todo?
 row_split_{{ env_id }} <- NULL
 
@@ -64,7 +79,14 @@ i2dash.scrnaseq::ComplexHeatmap_heatmap(
     column_split = split_by_{{ env_id }},
     row_split = row_split_{{ env_id }},
     column_names_rot = column_names_rot_{{ env_id }},
-    cell_fun = cell_fun_{{ env_id }}
+    cell_fun = cell_fun_{{ env_id }},
+    top_annotation = ha_{{ env_id }},
+    column_title = {{ env_id }}$column_title,
+    row_title = {{ env_id }}$row_title,
+    row_names_gp = grid::gpar(fontsize = 7),
+    column_names_gp = grid::gpar(fontsize = 7),
+    column_title_gp = grid::gpar(fontsize = 10),
+    row_title_gp = grid::gpar(fontsize = 10)
   )
 ```
 
@@ -101,13 +123,13 @@ exprs_values_{{ env_id }} <- shiny::reactive({
   
   # Subset features
   exprs_values <- subset_features(exprs_values, input$select_subset_{{ env_id }})
+  if (!{{ env_id }}$show_column_labels) colnames(exprs_values) <- c()
   
   # Handle aggregation
   if (input$select_aggregate_by_{{ env_id }} != "None") {
     exprs_values <- aggregate_data(exprs_values, {{ env_id }}$aggregate_by[, input$select_aggregate_by_{{ env_id }}])
   }
   
-  colnames(exprs_values) <- c()
   return(exprs_values)
 })
 
@@ -130,10 +152,9 @@ clust_{{ env_id }} <- shiny::reactive({
 })
 
 split_by_{{ env_id }} <- shiny::reactive({
-  if(input$select_split_by_{{ env_id }} == "None") {
+  if(input$select_split_by_{{ env_id }} == "None" | input$select_aggregate_by_{{ env_id }} != "None") {
     return(NULL)
   }
-  
   return({{ env_id }}$split_by[[input$select_split_by_{{ env_id }}]])
 })
 
@@ -145,11 +166,26 @@ output$plot_{{ env_id }} <- shiny::renderPlot({
   clustdist <- input$select_clustdist_{{ env_id }}
   clustmethod <- input$select_clustmethod_{{ env_id }}
   cell_fun <- NULL
-  column_names_rot = NULL
+  column_names_rot = 45
+  ha = NULL
       
   if (input$select_aggregate_by_{{ env_id }} != "None") {
     cell_fun <- function(j, i, x, y, width, height, fill) {grid::grid.text(sprintf("%.1f", exprs_values_{{ env_id }}()[i, j]), x, y, gp = grid::gpar(fontsize = 10))}
-    column_names_rot <- 45
+  }
+  
+  # Handle annotation
+  if (input$select_split_by_{{ env_id }} != "None" & input$select_aggregate_by_{{ env_id }} == "None") {
+    # Use colors defined by colormap else use random colors
+    if (input$select_split_by_{{ env_id }} %in% names(colormaps)){
+      ha = ComplexHeatmap::HeatmapAnnotation(
+        group_by = split_by_{{ env_id }}(),
+        col = list(group_by = colormaps[[input$select_split_by_{{ env_id }}]])
+      )
+    } else {
+      ha = ComplexHeatmap::HeatmapAnnotation(
+        group_by = split_by_{{ env_id }}()
+      )
+    }
   }
   
   i2dash.scrnaseq::ComplexHeatmap_heatmap(
@@ -163,7 +199,10 @@ output$plot_{{ env_id }} <- shiny::renderPlot({
     clustering_method_columns = clustmethod,
     column_split = split_by_{{ env_id }}(),
     column_names_rot = column_names_rot,
-    cell_fun = cell_fun
+    cell_fun = cell_fun,
+    top_annotation = ha,
+    column_title = {{ env_id }}$column_title,
+    row_title = {{ env_id }}$row_title
   )
 })
 
@@ -190,3 +229,4 @@ shiny::fillRow(flex = c(NA, 1),
       shiny::plotOutput("plot_{{ env_id }}", width = "100%", height = "400px")
 )
 ```
+
diff --git a/inst/templates/multiplot_template.Rmd b/inst/templates/multiplot_template.Rmd
deleted file mode 100644
index 3b7fbbb71790b49ced1581cdaeb65490ca26ecac..0000000000000000000000000000000000000000
--- a/inst/templates/multiplot_template.Rmd
+++ /dev/null
@@ -1,58 +0,0 @@
-
-### {{plot_title}}
-
-<!-- Component created on {{ date }} -->
-```{r}
-{{ env_id }} = readRDS(file.path(datadir, "{{ env_id }}.rds"))
-df <- data.frame({{ env_id }}$x, {{ env_id }}$y, {{ env_id }}$color_by)
-df.melted <- reshape::melt(df, id=c(names({{ env_id }}$y), names({{ env_id }}$color_by)))
-
-plotly::plotlyOutput("plot_{{ env_id }}")
-output$plot_{{ env_id }} <- plotly::renderPlotly({
-
-  if (!{{ env_id }}$y_selection){
-    index_y <- match(names({{ env_id }}$y), names(df.melted))
-  } else {
-    index_y <- match(input$select_y_{{ env_id }}, names(df.melted))
-  }
-
-  if (!{{ env_id }}$color_selection){
-    index_color <- match(names({{ env_id }}$color_by), names(df.melted))
-  } else {
-    index_color <- match(input$select_color_{{ env_id }}, names(df.melted))
-  }
-  
-  g1 <- ggplot2::ggplot(df.melted, mapping = ggplot2::aes(y = value, x = df.melted[,index_y], color = df.melted[,index_color])) +
-    ggplot2::geom_jitter() +
-    ggplot2::geom_violin(mapping = ggplot2::aes(y = value), scale = "count") +
-    ggplot2::facet_grid(. ~ variable, scales = "free_x") +
-    ggplot2::theme_bw() +
-    ggplot2::theme(panel.border = ggplot2::element_blank()) +
-    ggplot2::scale_colour_viridis_c() +
-    ggplot2::coord_flip() +
-    ggplot2::labs( y="Type", x="", color=names(df.melted)[index_color])
-
-  plotly::ggplotly(g1)
-})
-```
-
-***
-
-```{r}
-# selection field for y
-if ({{ env_id }}$y_selection){
-  selectInput("select_y_{{ env_id }}", label = "Select data for y axis:",
-            choices = names({{ env_id }}$y))
-}
-
-# selection field for color_by
-if ({{ env_id }}$color_selection){
-  selectInput("select_color_{{ env_id }}", label = "Select experimental factor for coloring:",
-            choices = names({{ env_id }}$color_by))
-}
-```
-
-Plot description:
-
-Sequencing is called *saturated* when generating more sequencing output from a cDNA library does not substantially increase the number of detected features in a sample. Since the number of detected features can act as a technical confounder, and thereby drive substructure in the data, it is advisable to aim for a saturated sequencing by either adding more sequencing output or decreasing the number of samples until saturation is achieved. [@zhang_one_2018] gives advise on how to choose the optimal cell number given a fixed sequencing budget
-
diff --git a/inst/templates/scatterplot.Rmd b/inst/templates/scatterplot.Rmd
index 9ce4424745c1cb7ace63877eed12b95daff3cde8..4adac29cae106a527d88690ccbb1c5e95ff640e4 100644
--- a/inst/templates/scatterplot.Rmd
+++ b/inst/templates/scatterplot.Rmd
@@ -16,8 +16,18 @@ if(!is.null({{ env_id }}$y_title)) y_title_{{ env_id }} <- {{ env_id }}$y_title
 if(!is.null({{ env_id }}$colour_by)) colour_by_{{ env_id }} <- {{ env_id }}$colour_by[,1] else colour_by_{{ env_id }} <- NULL
 if(!is.null({{ env_id }}$labels)) labels_{{ env_id }} <- {{ env_id }}$labels else labels_{{ env_id }} <- rownames({{ env_id }}$x)
 
+# compare with colormaps
+if(is.factor(colour_by_{{ env_id }})){
+  if(colnames({{ env_id }}$colour_by)[1] %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[colnames({{ env_id }}$colour_by)[1]]] else colors_{{ env_id }} <- NULL
+} else {
+  colors_{{ env_id }} <- NULL
+}
+
 # creating the plot object
-plot_{{ env_id }} <- i2dash.scrnaseq::plotly_scatterplot(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], color = colour_by_{{ env_id }}, text = labels_{{ env_id }}, y_title = y_title_{{ env_id }}, x_title = x_title_{{ env_id }})
+plot_{{ env_id }} <- i2dash.scrnaseq::plotly_scatterplot(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], color = colour_by_{{ env_id }}, text = labels_{{ env_id }}, y_title = y_title_{{ env_id }}, x_title = x_title_{{ env_id }}, colors = colors_{{ env_id }}, type = "scatter") %>%
+  plotly::layout(
+    title = {{ env_id }}$plot_title
+  ) %>% plotly::toWebGL()
 
 # Provide data for download
 if(is.null({{ env_id }}$colour_by)){
@@ -108,17 +118,19 @@ y_{{ env_id }} <- shiny::reactive({
 colour_{{ env_id }} <- shiny::reactive({
   if(length({{ env_id }}$colouring) > 1){
     # "No colour" in radio menu
-    if(input$radio_{{ env_id }} == 0){ 
-      return(list(colour = NULL, annotation = NULL))
+    if(input$radio_{{ env_id }} == 0){
+      return(list(colour = NULL, annotation = NULL, colour_title = NULL))
     
     # "Colour by metadata" in radio menu
     } else if(input$radio_{{ env_id }} == 1){
       if(!{{ env_id }}$colour_by_selection){
         data <- {{ env_id }}$colour_by[[1]]
+        title <- colnames({{ env_id }}$colour_by)[1]
       } else {
         data <- {{ env_id }}$colour_by[[input$select_colour_{{ env_id }}]]
+        title <- input$select_colour_{{ env_id }}
       }
-      return(list(colour = data, annotation = NULL))
+      return(list(colour = data, annotation = NULL, colour_title = title))
     
     # "Colour by label" in radio menu
     } else if(input$radio_{{ env_id }} == 2){
@@ -135,18 +147,19 @@ colour_{{ env_id }} <- shiny::reactive({
         ax = 20,
         ay = -40
       )
-      return(list(colour = NULL, annotation = a))
+      return(list(colour = NULL, annotation = a, colour_title = NULL))
       
     # "Colour by expression" in radio menu
     } else if(input$radio_{{ env_id }} == 3){
       data <- {{ env_id }}$exprs_values[input$select_feature_{{ env_id }},]
-      return(list(colour = data, annotation = NULL))
+      return(list(colour = data, annotation = NULL, colour_title = input$select_feature_{{ env_id }}))
     }
   } else {
-    return(list(colour = NULL, annotation = NULL))
+    return(list(colour = NULL, annotation = NULL, colour_title = NULL))
   }
 })
 
+
 #
 # Download data.frame
 #
@@ -166,14 +179,23 @@ output$downloadData_{{ env_id }} <- downloadHandler(
 # reactive plot creation
 #
 output$plot_{{ env_id }} <- plotly::renderPlotly({
+  # compare with colormaps
+  if(is.factor(colour_{{ env_id }}()$colour)){
+    if(colour_{{ env_id }}()$colour_title %in% names(colormaps)) colors <- colormaps[[colour_{{ env_id }}()$colour_title]] else colors <- "Set1"
+  } else {
+    colors <- NULL
+  }
+
+  
   if(!is.null({{ env_id }}$y_title)) y_title <- {{ env_id }}$y_title else y_title <- y_{{ env_id }}()$title
   if(!is.null({{ env_id }}$x_title)) x_title <- {{ env_id }}$x_title else x_title <- x_{{ env_id }}()$title
   if(!is.null({{ env_id }}$labels)) labels <- {{ env_id }}$labels else labels <- rownames({{ env_id }}$x)
   
-  i2dash.scrnaseq::plotly_scatterplot(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, color = colour_{{ env_id }}()$colour, text = labels, y_title = y_title, x_title = x_title) %>%
+  i2dash.scrnaseq::plotly_scatterplot(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, color = colour_{{ env_id }}()$colour, text = labels, y_title = y_title, x_title = x_title, colors = colors, type = "scatter") %>%
     plotly::layout(
-      annotations = colour_{{ env_id }}()$annotation
-    )
+      annotations = colour_{{ env_id }}()$annotation,
+      title = {{ env_id }}$plot_title
+    ) %>% plotly::toWebGL()
 })
 
 #
diff --git a/inst/templates/violinplot.Rmd b/inst/templates/violinplot.Rmd
index b8d1e4d68ff73686bc590a7970619a9a79bef11b..ccd03d3a82a7f046d34126fee47ead93226d5f3a 100644
--- a/inst/templates/violinplot.Rmd
+++ b/inst/templates/violinplot.Rmd
@@ -26,13 +26,19 @@ if(!is.null({{ env_id }}$group_by)){
   group_by_title_{{ env_id }} <- NULL
   group_by_{{ env_id }} <- NULL
 }
+# compare with colormaps
+if(!is.null(group_by_title_{{ env_id }})){
+  if(group_by_title_{{ env_id }} %in% names(colormaps)) colors_{{ env_id }} <- colormaps[[group_by_title_{{ env_id }}]] else colors_{{ env_id }} <- "Set1"
+} else {
+  colors_{{ env_id }} <- "Set1"
+}
 
 # set title variables
 if(!is.null({{ env_id }}$y_title)) y_title_{{ env_id }} <- {{ env_id }}$y_title else y_title_{{ env_id }} <- colnames(y_{{ env_id }})
 if(!is.null({{ env_id }}$group_by_title)) group_by_title_{{ env_id }} <- {{ env_id }}$group_by_title
 
 # creating the plot object
-plot_{{ env_id }} <- i2dash.scrnaseq::plotly_violinplot(y = y_{{ env_id }}[,1], group_by = group_by_{{ env_id }}[,1], y_title = y_title_{{ env_id }}, group_by_title = group_by_title_{{ env_id }})
+plot_{{ env_id }} <- i2dash.scrnaseq::plotly_violinplot(y = y_{{ env_id }}[,1], group_by = group_by_{{ env_id }}[,1], y_title = y_title_{{ env_id }}, group_by_title = group_by_title_{{ env_id }}, colors = colors_{{ env_id }})
 
 # Provide data for download
 if(!is.null({{ env_id }}$group_by)) download_df <- data.frame(y_{{ env_id }}, group_by_{{ env_id }}) else download_df <- data.frame(y_{{ env_id }})
@@ -103,7 +109,12 @@ if( !{{ env_id }}$group_by_selection ) {
 output$downloadData_{{ env_id }} <- downloadHandler(
   filename =  paste('data-', Sys.Date(), '.csv', sep=''),
   content = function(file) {
-    write.csv(data.frame(y_{{ env_id }}()$data, group_by_{{ env_id }}()$data), file)
+    if(is.null({{ env_id }}$group_by)){
+      df <- y_{{ env_id }}()$data
+    } else {
+      df <- data.frame(y_{{ env_id }}()$data, group_by_{{ env_id }}()$data)
+    }
+    write.csv(df, file)
   }
 )
 
@@ -111,18 +122,18 @@ output$downloadData_{{ env_id }} <- downloadHandler(
 # reactive for plot creation
 #
 output$plot_{{ env_id }} <- plotly::renderPlotly({
-  if(!is.null({{ env_id }}$y_title)){
-    y_title <- {{ env_id }}$y_title
+  # compare with colormaps
+  if(!is.null(group_by_{{ env_id }}()$title)){
+    if(group_by_{{ env_id }}()$title %in% names(colormaps)) colors <- colormaps[[group_by_{{ env_id }}()$title]] else colors <- "Set1"
   } else {
-    y_title <- y_{{ env_id }}()$title
-  }
-  if(!is.null({{ env_id }}$group_by_title)){
-    group_by_title <- {{ env_id }}$group_by_title
-  } else {
-    group_by_title <- group_by_{{ env_id }}()$title
+    colors <- "Set1"
   }
   
-  i2dash.scrnaseq::plotly_violinplot(y = y_{{ env_id }}()$data, group_by = group_by_{{ env_id }}()$data, y_title = y_title, group_by_title = group_by_title)
+  # set custom axis titles if provided
+  if(!is.null({{ env_id }}$y_title)) y_title <- {{ env_id }}$y_title else y_title <- y_{{ env_id }}()$title
+  if(!is.null({{ env_id }}$group_by_title)) group_by_title <- {{ env_id }}$group_by_title else group_by_title <- group_by_{{ env_id }}()$title
+  
+  i2dash.scrnaseq::plotly_violinplot(y = y_{{ env_id }}()$data, group_by = group_by_{{ env_id }}()$data, y_title = y_title, group_by_title = group_by_title, colors = colors)
 })
 
 #
diff --git a/man/ComplexHeatmap_heatmap.Rd b/man/ComplexHeatmap_heatmap.Rd
index c1a1300529f793d3aa312b5a78f80703e6c78d11..ad7a7e0d21936f52a5ff943f53c5f2a692118855 100644
--- a/man/ComplexHeatmap_heatmap.Rd
+++ b/man/ComplexHeatmap_heatmap.Rd
@@ -7,7 +7,7 @@
 ComplexHeatmap_heatmap(..., legend_title = NULL)
 }
 \arguments{
-\item{...}{further optional and valid arguments, that are the supported arguments in ComplexHeatmap.}
+\item{...}{further optional arguments, that are the supported arguments in \code{ComplexHeatmap}.}
 
 \item{legend_title}{An optional title of the legend.}
 }
diff --git a/man/add_feature_expression_page.Rd b/man/add_feature_expression_page.Rd
index 0d90cb01d4c363dd10f21b004d223a4b42ba73df..2f21b9d0c82a5fb801ccf6c4805277dd99c5fc3f 100644
--- a/man/add_feature_expression_page.Rd
+++ b/man/add_feature_expression_page.Rd
@@ -6,7 +6,7 @@
 \alias{add_feature_expression_page,i2dashboard,missing-method}
 \alias{add_feature_expression_page,i2dashboard,SingleCellExperiment-method}
 \alias{add_feature_expression_page,i2dashboard,Seurat-method}
-\title{Add a gene expression page.}
+\title{Add a feature expression page.}
 \usage{
 add_feature_expression_page(dashboard, object, ...)
 
@@ -46,6 +46,10 @@ add_feature_expression_page(dashboard, object, ...)
 \item{assay}{A character vector specifying which assay from \code{object@assays} to obtain expression values from (see Details).}
 
 \item{slot}{A character vector specifying the name of the slot in the assay.}
+
+\item{page}{The name of the page to be added.}
+
+\item{assay_slot}{A character specifying the name of the data slot in the assay. (Default: "data")}
 }
 \value{
 An object of class \linkS4class{i2dash::i2dashboard}.
@@ -62,7 +66,7 @@ The parameters \code{use_dimred}, \code{exprs_values} (or \code{assay}) and \cod
   In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
   In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
   \itemize{
-    \item the name of a item in \code{reducedDims(object)} or \code{object@reductions},
+    \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
     \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
     \item column names of \code{colData(object)} or \code{object@meta.data}.
   }
diff --git a/man/barplot.Rd b/man/barplot.Rd
index d309464be02e23c64c0b26716e2faab94b05eaff..6d0b2b3230ff21e1f39940aba623d2deda801db9 100644
--- a/man/barplot.Rd
+++ b/man/barplot.Rd
@@ -5,6 +5,7 @@
 \alias{barplot}
 \alias{barplot,i2dashboard,missing-method}
 \alias{barplot,i2dashboard,SingleCellExperiment-method}
+\alias{barplot,i2dashboard,Seurat-method}
 \title{Renders a component containing a horizontal barplot.}
 \usage{
 barplot(dashboard, object, ...)
@@ -14,16 +15,21 @@ barplot(dashboard, object, ...)
   y_group_by_title = NULL)
 
 \S4method{barplot}{i2dashboard,SingleCellExperiment}(dashboard, object,
-  use = "colData", y_group_by = NULL, x_group_by = NULL, ...)
+  use = c("colData", "rowData"), y_group_by = NULL,
+  x_group_by = NULL, ...)
+
+\S4method{barplot}{i2dashboard,Seurat}(dashboard, object,
+  use = c("meta.data", "meta.features"), assay = "RNA",
+  y_group_by = NULL, x_group_by = NULL, ...)
 }
 \arguments{
 \item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
 
-\item{object}{A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.}
+\item{object}{An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.}
 
-\item{y_group_by}{A data.frame (matrix) with columns containing grouping factors for the vertical axis.}
+\item{y_group_by}{A data.frame (matrix) with columns containing grouping factors for the vertical axis or a character vector indicating the columns to use from \code{use}.}
 
-\item{x_group_by}{Optionally provide a data.frame (matrix) with columns containing grouping factors for the horizontal axis. The result is a barplot grouped by the levels in \code{x_group_by} and shows the relative number of its observations.}
+\item{x_group_by}{Optionally provide a data.frame (matrix) with columns containing grouping factors for the horizontal axis or a character vector indicating the columns to use from \code{use}. The result is a barplot grouped by the levels in \code{x_group_by}, which shows the relative number of its observations.}
 
 \item{title}{The title of the components junk.}
 
@@ -31,11 +37,15 @@ barplot(dashboard, object, ...)
 
 \item{y_group_by_title}{The title of the y-axis.}
 
-\item{use}{A character specifying where to obtain the data from. One of \code{"colData"} or \code{"rowData"}.}
+\item{use}{A character specifying where to obtain the data from. Valid input for SingleCellExperiment object: ("colData", "rowData"). Valid input for Seurat object: ("meta.data" for sample metadata, "meta.feature" for feature metadata.)}
+
+\item{assay}{Necessery, if \code{use} = "meta.feature". A character defining the assay to obtain the feature metadata from (default "RNA").}
 }
 \value{
 A string containing markdown code for the rendered component
 
+An object of class \linkS4class{i2dash::i2dashboard}.
+
 An object of class \linkS4class{i2dash::i2dashboard}.
 }
 \description{
diff --git a/man/boxplot.Rd b/man/boxplot.Rd
index 22484c8a94c2bc1317209cdfeb056a5e053952c7..a679b771773ca47b05565cf74c2fe0717362e8ef 100644
--- a/man/boxplot.Rd
+++ b/man/boxplot.Rd
@@ -5,6 +5,7 @@
 \alias{boxplot}
 \alias{boxplot,i2dashboard,missing-method}
 \alias{boxplot,i2dashboard,SingleCellExperiment-method}
+\alias{boxplot,i2dashboard,Seurat-method}
 \title{Renders a component containing a boxplot}
 \usage{
 boxplot(dashboard, object, ...)
@@ -13,17 +14,20 @@ boxplot(dashboard, object, ...)
   title = NULL, x_title = NULL, group_by_title = NULL)
 
 \S4method{boxplot}{i2dashboard,SingleCellExperiment}(dashboard, object,
-  use = "colData", x = NULL, group_by = NULL, title = NULL,
-  x_title = NULL, group_by_title = NULL)
+  use = c("colData", "rowData"), x = NULL, group_by = NULL, ...)
+
+\S4method{boxplot}{i2dashboard,Seurat}(dashboard, object,
+  use = c("meta.data", "meta.features"), assay = "RNA", x = NULL,
+  group_by = NULL, ...)
 }
 \arguments{
 \item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
 
-\item{object}{A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.}
+\item{object}{An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.}
 
-\item{x}{A data.frame (matrix) containing numeric observations for the horizontal axis, or a character vector indicating column names of \code{colData(object)}, \code{rowData(object)}.}
+\item{x}{A data.frame (matrix) containing numeric observations for the horizontal axis or a character vector indicating the columns to use from \code{use}.}
 
-\item{group_by}{An optional data.frame (matrix) with columns containing grouping factors for the vertical axis.}
+\item{group_by}{An optional data.frame (matrix) with columns containing grouping factors for the vertical axis or a character vector indicating the columns to use from \code{use}}
 
 \item{title}{The title of the components junk.}
 
@@ -31,11 +35,15 @@ boxplot(dashboard, object, ...)
 
 \item{group_by_title}{The title of the y-axis.}
 
-\item{use}{A character specifying where to obtain the data from. One of \code{"colData"} or \code{"rowData"}.}
+\item{use}{A character specifying where to obtain the data from. Valid input for SingleCellExperiment object: ("colData", "rowData"). Valid input for Seurat object: ("meta.data" for sample metadata, "meta.feature" for feature metadata.)}
+
+\item{assay}{Necessery, if \code{use} = "meta.feature". A character defining the assay to obtain the feature metadata from (default "RNA").}
 }
 \value{
 A string containing markdown code for the rendered component
 
+An object of class \linkS4class{i2dash::i2dashboard}.
+
 An object of class \linkS4class{i2dash::i2dashboard}.
 }
 \description{
diff --git a/man/bubbleplot.Rd b/man/bubbleplot.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..21758b25e5575bc4772bd405577d10092e1851e8
--- /dev/null
+++ b/man/bubbleplot.Rd
@@ -0,0 +1,34 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/bubbleplot.R
+\name{bubbleplot}
+\alias{bubbleplot}
+\title{Renders a component containing a bubbleplot with optional selection options}
+\usage{
+bubbleplot(dashboard, x, y, size, colour_by = NULL, labels = NULL,
+  title = NULL, x_title = NULL, y_title = NULL)
+}
+\arguments{
+\item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
+
+\item{x}{A data.frame (matrix) containing columns with numeric values that will be mapped to the x-axis.}
+
+\item{y}{A data.frame (matrix) containing columns with numeric values that will be mapped to the y-axis.}
+
+\item{size}{A ata.frame (matrix) containing columns with numeric values that describe the size of the bubbles.}
+
+\item{colour_by}{An optional data.frame (matrix) containing columns with numeric or factorial values that will be used for colouring.}
+
+\item{labels}{An optional vector with sample names.}
+
+\item{title}{The title of the components junk.}
+
+\item{x_title}{An optional title of the x-axis. If not provided the column names from \code{x} are used instead.}
+
+\item{y_title}{An optional title of the y-axis. If not provided the column names from \code{y}  are used instead.}
+}
+\value{
+A string containing markdown code for the rendered component
+}
+\description{
+Renders a component containing a bubbleplot with optional selection options
+}
diff --git a/man/dimred-feature-page.Rd b/man/dimred-feature-page.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..1be02d4168314b6bf65b1f6eac02bdfd6706b6e5
--- /dev/null
+++ b/man/dimred-feature-page.Rd
@@ -0,0 +1,60 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/AllGenerics.R, R/dimred_feature_page.R
+\docType{methods}
+\name{dimred-feature-page}
+\alias{dimred-feature-page}
+\alias{add_dimred_feature_page}
+\title{View a dimension reduction side-by-side with feature metadata}
+\usage{
+add_dimred_feature_page(dashboard, object, ...)
+
+\S4method{add_dimred_feature_page}{i2dashboard,missing}(dashboard,
+  use_dimred, exprs_values, feature_metadata,
+  page = "dimred_feature_page",
+  title = "Dim. reduction & deature metadata", menu = NULL)
+
+
+  \S4method{add_dimred_feature_page}{i2dashboard,SingleCellExperiment}(dashboard,
+  object, use_dimred, exprs_values, feature_metadata, subset_row, ...)
+
+\S4method{add_dimred_feature_page}{i2dashboard,Seurat}(dashboard, object,
+  use_dimred, feature_metadata, subset_row, assay = "RNA",
+  assay_slot = "data", ...)
+}
+\arguments{
+\item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
+
+\item{object}{A \linkS4class{SingleCellExperiment::SingleCellExperiment} object or a \linkS4class{Seurat::Seurat} object.}
+
+\item{use_dimred}{Coordinates of the reduced dimensions, used for the scatterplot (see Details).}
+
+\item{exprs_values}{Expression data of features of interest in rows and samples in columns (see Details).}
+
+\item{feature_metadata}{A data.frame (matrix) along rows of \code{exprs_values} containing feature metadata, or a character vector indicating columns from \code{rowData(object)} or \code{object[[assay]]@feature.data}.}
+
+\item{page}{The name of the page to be added.}
+
+\item{title}{The title of the page.}
+
+\item{menu}{(Optional) The name of the menu, under which the page should appear.}
+
+\item{assay}{A character specifying the assay (\code{object@assays}) to obtain expression values from. (Default: "RNA")}
+
+\item{assay_slot}{A character specifying the name of the data slot in the assay. (Default: "data")}
+}
+\value{
+An object of class \linkS4class{i2dash::i2dashboard}.
+}
+\description{
+The dimension reduction plot is colored by feature expression and updated if the users clicks feature rows in the metadata table.
+}
+\details{
+The parameters \code{use_dimred}, \code{exprs_values} (or \code{assay}) and \code{group_by} take different arguments depending on the class of \code{object}.
+  In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
+  In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
+  \itemize{
+    \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
+    \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
+    \item column names of \code{colData(object)} or \code{object@meta.data}.
+  }
+}
diff --git a/man/dimred-metadata-page.Rd b/man/dimred-metadata-page.Rd
deleted file mode 100644
index a1f84170fcfa65c5997cf01c1dc797dad0bc4b69..0000000000000000000000000000000000000000
--- a/man/dimred-metadata-page.Rd
+++ /dev/null
@@ -1,77 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/AllGenerics.R, R/dimension_reduction_page.R,
-%   R/dimred_metadata_page.R
-\docType{methods}
-\name{dimred-metadata-page}
-\alias{dimred-metadata-page}
-\alias{add_dimred_metadata_page}
-\alias{add_dimred_feature_page}
-\title{Add a dimension reduction page with feature metadata}
-\usage{
-add_dimred_feature_page(dashboard, object, ...)
-
-add_dimred_metadata_page(dashboard, object, ...)
-
-\S4method{add_dimred_metadata_page}{i2dashboard,missing}(dashboard,
-  use_dimred, metadata, group_by, title = NULL, labels = NULL,
-  show_group_sizes = TRUE, show_silhouette = FALSE, menu = NULL)
-
-\S4method{add_dimred_feature_page}{i2dashboard,missing}(dashboard,
-  use_dimred, exprs_values, feature_metadata,
-  title = "Marker gene expression", menu = NULL)
-
-
-  \S4method{add_dimred_feature_page}{i2dashboard,SingleCellExperiment}(dashboard,
-  object, use_dimred, exprs_values, feature_metadata, subset_row,
-  title = "Marker gene expression", menu = NULL)
-
-\S4method{add_dimred_feature_page}{i2dashboard,Seurat}(dashboard, object,
-  use_dimred, exprs_values, feature_metadata, subset_row, assay,
-  assay_slot = "data", title = "Marker gene expression", menu = NULL)
-}
-\arguments{
-\item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
-
-\item{object}{A \linkS4class{SingleCellExperiment::SingleCellExperiment} object or a \linkS4class{Seurat::Seurat} object.}
-
-\item{use_dimred}{A list of data.frames (matrices) or a single data.frame (matrix) containing coordinates of the reduced dimensions, a character vector representing valid \code{reducedDim} slots of \code{object} or names of the \linkS4class{Seurat::DimReduc} object in \code{object@reductions}.}
-
-\item{metadata}{A data.frame (matrix) containing metadata (e.g. cluster, timepoint, number of features, etc) along samples.}
-
-\item{group_by}{A string indicating a column in \code{metadata} that is used to group observations.}
-
-\item{title}{The title of the page.}
-
-\item{labels}{An optional vector with sample labels.}
-
-\item{show_group_sizes}{A logical value indicating if a barplot showing the number of observations from \code{group_by} will be creaed (default \code{TRUE}).}
-
-\item{show_silhouette}{A logical value indicating if a silhouette plot should be shown (default \code{FALSE}).}
-
-\item{menu}{(Optional) The name of the menu, under which the page should appear.}
-
-\item{exprs_values}{A data.frame (matrix) containing expression data of features of interest in rows and samples in columns, or a string representing the name of an \code{assay} of \code{object}.}
-
-\item{feature_metadata}{A data.frame (matrix) along rows of \code{exprs_values} containing feature metadata, or a character vector indicating columns from \code{rowData(object)} or \code{object@meta.data}.}
-
-\item{assay}{A character vector specifying which assay from \code{object@assays} to obtain expression values from.}
-
-\item{slot}{A character vector specifying the name of the slot in the assay.}
-
-\item{dashboard}{A \linkS4class{i2dash::i2dashboard}.}
-
-\item{use_dimred}{A data.frame (matrix) containing coordinates of the reduced dimensions. Rownames are used as sample labels.}
-
-\item{title}{The title of the page.}
-
-\item{menu}{The name of the menu, under which the page should appear.}
-}
-\value{
-An object of class \linkS4class{i2dash::i2dashboard}.
-}
-\description{
-This function adds a page with two linked components to the \code{dashboard} object: A scatterplot, showing samples in along two-dimensional coordinates, and a table, showing feature metadata. A click on a feature in the table updates the scatterplot with the feature expression.
-
-Creates a page with up to four different linked components, including a scatterplot for dimension reductions, a bar plot showing numbers of observations by group, and a silhouette plot to assess grouping consistency.
-Additional sample metadata is visualized using boxplots and barplots, depending on the data type of the underlying variable.
-}
diff --git a/man/dimred-sample-page.Rd b/man/dimred-sample-page.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..3bd681df545ee65acc178f002ab26ccddf8d1533
--- /dev/null
+++ b/man/dimred-sample-page.Rd
@@ -0,0 +1,62 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/AllGenerics.R, R/dimred_sample_page.R
+\docType{methods}
+\name{dimred-sample-page}
+\alias{dimred-sample-page}
+\alias{add_dimred_sample_page}
+\title{Characterize and visualize dimension reductions and sample groupings / metadata.}
+\usage{
+add_dimred_sample_page(dashboard, object, ...)
+
+\S4method{add_dimred_sample_page}{i2dashboard,missing}(dashboard,
+  use_dimred, sample_metadata, group_by, page = "dimred_sample_page",
+  title = "Dim. reduction & sample metadata", labels = NULL,
+  show_group_sizes = TRUE, show_silhouette = FALSE, menu = NULL)
+
+
+  \S4method{add_dimred_sample_page}{i2dashboard,SingleCellExperiment}(dashboard,
+  object, use_dimred, sample_metadata, ...)
+
+\S4method{add_dimred_sample_page}{i2dashboard,Seurat}(dashboard, object,
+  use_dimred, sample_metadata, ...)
+}
+\arguments{
+\item{dashboard}{A \linkS4class{i2dash::i2dashboard}.}
+
+\item{object}{An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.}
+
+\item{use_dimred}{A data.frame (matrix) containing coordinates of the reduced dimensions or a string indicating a dimension reduction from "reductions" of a Seurat \code{object}. Rownames are used as sample labels.}
+
+\item{sample_metadata}{Sample metadata in columns and samples in rows (see Details).}
+
+\item{group_by}{A string indicating a column in \code{metadata} that is used to group observations.}
+
+\item{page}{The name of the page to be added.}
+
+\item{title}{The title of the page.}
+
+\item{labels}{An optional vector with sample labels.}
+
+\item{show_group_sizes}{A logical value indicating if a barplot showing the number of observations from \code{group_by} will be created (default \code{TRUE}).}
+
+\item{show_silhouette}{A logical value indicating if a silhouette plot should be shown (default \code{FALSE}).}
+
+\item{menu}{The name of the menu, under which the page should appear.}
+}
+\value{
+An object of class \linkS4class{i2dash::i2dashboard}.
+}
+\description{
+Creates a page with up to four different linked components, including a scatterplot for dimension reductions, a bar plot showing numbers of observations by group, and a silhouette plot to assess grouping consistency.
+Additional sample metadata is visualized using boxplots and barplots, depending on the data type of the underlying variable.
+}
+\details{
+The parameters \code{use_dimred}, \code{sample_metadata} (or \code{assay}) and \code{group_by} take different arguments depending on the class of \code{object}.
+  In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
+  In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
+  \itemize{
+    \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
+    \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
+    \item column names of \code{colData(object)} or \code{object@meta.data}.
+  }
+}
diff --git a/man/feature-grid-page.Rd b/man/feature-grid-page.Rd
index 24334d53f4847324e159ec75f0b97f61f8a11fa0..d70f5e6532d1fcd1e4c5528c84edd5d750166cef 100644
--- a/man/feature-grid-page.Rd
+++ b/man/feature-grid-page.Rd
@@ -1,40 +1,34 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/AllGenerics.R, R/feature_grid_page.R,
-%   R/multi_gene_expression_page.R
+% Please edit documentation in R/AllGenerics.R, R/feature_grid_page.R
 \docType{methods}
 \name{feature-grid-page}
 \alias{feature-grid-page}
 \alias{add_feature_grid_page}
 \title{Create expression visualization for multiple selected features on a regular grid.}
 \usage{
-add_feature_grid_page(report, object, ...)
+add_feature_grid_page(dashboard, object, ...)
 
-\S4method{add_feature_grid_page}{i2dashboard,missing}(report, use_dimred,
-  exprs_values, title = "Feature grid", menu = "Tools")
+\S4method{add_feature_grid_page}{i2dashboard,missing}(dashboard,
+  use_dimred, exprs_values, page = "feature_grid_page",
+  title = "Feature grid", menu = "Tools")
 
-\S4method{add_feature_grid_page}{i2dashboard,SingleCellExperiment}(report,
-  object, use_dimred, exprs_values, subset_row = NULL, ...)
-
-\S4method{add_feature_grid_page}{i2dashboard,Seurat}(report, object,
-  use_dimred, assay, slot = "data", subset_row = NULL)
 
-\S4method{add_feature_grid_page}{i2dashboard,missing}(report, use_dimred,
-  exprs_values, title = "Feature grid", menu = "Tools")
-
-\S4method{add_feature_grid_page}{i2dashboard,SingleCellExperiment}(report,
+  \S4method{add_feature_grid_page}{i2dashboard,SingleCellExperiment}(dashboard,
   object, use_dimred, exprs_values, subset_row = NULL, ...)
 
-\S4method{add_feature_grid_page}{i2dashboard,Seurat}(report, object,
-  use_dimred, assay, slot = "data", subset_row = NULL)
+\S4method{add_feature_grid_page}{i2dashboard,Seurat}(dashboard, object,
+  use_dimred, assay, assay_slot = "data", subset_row = NULL, ...)
 }
 \arguments{
-\item{report}{A \linkS4class{i2dash::i2dashboard} report.}
+\item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
 
 \item{object}{A \linkS4class{SingleCellExperiment::SingleCellExperiment} object or a \linkS4class{Seurat::Seurat} object.}
 
-\item{use_dimred}{A list of data.frames (matrices) or a single data.frame (matrix) containing coordinates of the reduced dimensions, a character vector representing valid \code{reducedDim} slots of \code{object} or names of the \linkS4class{Seurat::DimReduc} object in \code{object@reductions}.}
+\item{use_dimred}{Coordinates of the reduced dimensions, used for the scatterplot (see Details).}
+
+\item{exprs_values}{Expression data of features of interest in rows and samples in columns (see Details).}
 
-\item{exprs_values}{A data.frame (matrix) containing expression data of features of interest in rows and samples in columns, or a string representing the name of an \code{assay} of \code{object}.}
+\item{page}{The name of the page to be added.}
 
 \item{title}{The title of the page.}
 
@@ -47,10 +41,18 @@ add_feature_grid_page(report, object, ...)
 \item{slot}{A character vector specifying the name of the slot in the assay.}
 }
 \value{
-An object of class \linkS4class{i2dash::i2dashboard}.
-
 An object of class \linkS4class{i2dash::i2dashboard}.
 }
 \description{
 Users can select features and a dimension reduction to plot feature expression values.
 }
+\details{
+The parameters \code{use_dimred}, \code{exprs_values} (or \code{assay}) and \code{group_by} take different arguments depending on the class of \code{object}.
+  In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters are expected to be of class \code{data.frame} or \code{matrix}.
+  In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} or \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}, containing
+  \itemize{
+    \item the name of an item in \code{reducedDims(object)} or \code{object@reductions},
+    \item a valid assay name from \code{assayNames(object)} or \code{names(object@assays)},
+    \item column names of \code{colData(object)} or \code{object@meta.data}.
+  }
+}
diff --git a/man/features_by_factors.Rd b/man/features_by_factors.Rd
deleted file mode 100644
index abab2e1b6f91fcf6b37619e6ea64f848990782cd..0000000000000000000000000000000000000000
--- a/man/features_by_factors.Rd
+++ /dev/null
@@ -1,23 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/features_by_factors.R
-\name{features_by_factors}
-\alias{features_by_factors}
-\title{Renders a features by factor violin plot}
-\usage{
-features_by_factors(object, x, y, title = "Features by factor")
-}
-\arguments{
-\item{object}{A \linkS4class{i2dash::i2dashboard} object.}
-
-\item{x}{A list with the x-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.}
-
-\item{y}{A list with the y-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.(Needs to be categorial. Horizontal violinplots are not possible.)}
-
-\item{title}{A title that will be displayed on top.}
-}
-\value{
-A string containing markdown code for the rendered textbox
-}
-\description{
-Renders a features by factor violin plot
-}
diff --git a/man/heatmap.Rd b/man/heatmap.Rd
index 2759d6966dc54532f8ac8969968cacc816f1bf31..169d8c67ed330481dfe5d6e4a57c8c42cf2a650c 100644
--- a/man/heatmap.Rd
+++ b/man/heatmap.Rd
@@ -5,10 +5,13 @@
 \alias{heatmap}
 \alias{heatmap,i2dashboard,missing-method}
 \alias{heatmap,i2dashboard,SingleCellExperiment-method}
-\title{Renders a component containing a \link[ComplexHeatmap]{Heatmap}.}
+\alias{heatmap,i2dashboard,Seurat-method}
+\title{Renders a component containing a heatmap based on \link[ComplexHeatmap]{Heatmap}.}
 \usage{
 heatmap(dashboard, object, ...)
 
+heatmap(dashboard, object, ...)
+
 \S4method{heatmap}{i2dashboard,missing}(dashboard, exprs_values,
   split_by = NULL, aggregate_by = NULL, title = NULL,
   legend = NULL, cluster_rows = FALSE, cluster_columns = FALSE,
@@ -17,15 +20,19 @@ heatmap(dashboard, object, ...)
   "single", "complete", "mcquitty", "median", "centroid"))
 
 \S4method{heatmap}{i2dashboard,SingleCellExperiment}(dashboard, object,
-  exprs_values = "counts", subset_row = NULL, split_by = NULL,
+  exprs_values = "counts", subset_row, split_by = NULL,
+  aggregate_by = NULL, ...)
+
+\S4method{heatmap}{i2dashboard,Seurat}(dashboard, object, assay = "RNA",
+  assay_slot = "data", subset_row, split_by = NULL,
   aggregate_by = NULL, ...)
 }
 \arguments{
 \item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
 
-\item{object}{A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.}
+\item{object}{An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.}
 
-\item{exprs_values}{A data.frame (matrix) containing expression data of features of interest in rows and samples in columns or a string representing the name of an \code{assay} of \code{object}.}
+\item{exprs_values}{A data.frame (matrix) containing expression data of features of interest in rows and samples in columns or a character indicating which assay of the \linkS4class{SingleCellExperiment::SingleCellExperiment} object to use.}
 
 \item{split_by}{An optional data.frame (matrix) containing grouping factors for spliting columns of the heatmap. In case of \code{i2dashboard,SingleCellExperiment}, should be column names of \code{colData(object)}.}
 
@@ -39,17 +46,47 @@ heatmap(dashboard, object, ...)
 
 \item{cluster_columns}{A logical controls whether to make cluster on columns.}
 
-\item{clustering_distance}{The distance measure to use for hierarchical clustering.}
+\item{clustering_distance}{A pre-defined character which is in ("euclidean", "maximum", "manhattan", "binary", "minkowski").}
 
-\item{clustering_method}{Method to perform hierarchical clustering, passed to \link[stats]{hclust}.}
+\item{clustering_method}{Method to perform hierarchical clustering, pass to \link[stats]{hclust} ("average", "ward.D", "ward.D2", "single", "complete", "mcquitty", "median","centroid").}
+
+\item{subset_row}{A character vector (of feature names), a logical vector or numeric vector (of indices) specifying the features to use.}
+
+\item{assay}{A character specifying the assay (\code{object@assays}) to obtain expression values from. (Default: "RNA")}
+
+\item{assay_slot}{A character specifying the name of the data slot in the assay. (Default: "data")}
+
+\item{column_split}{An optional data.frame (matrix) containing factorial metadata (e.g. cluster, timepoint, etc.) along samples for splitting the columns or a character vector indicating the columns to use from the "meta.data" of a Seurat \code{object}/ the columns to use from "colData" of a SingleCellExperiment. Enables a second visualisation option: heatmap with levels of \code{column_split} as columns and features of \code{exprs_values} as rows.}
+
+\item{visualisation_mode}{Used in case of the static mode and if \code{column_split} is provided: Select between "splitted" or "summarized" heatmap visualisation according to the levels in the 1. column of \code{column_split}.}
+
+\item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
+
+\item{exprs_values}{A data.frame (matrix) containing expression data of features of interest in rows and samples in columns or a string representing the name of an \code{assay} of \code{object}.}
+
+\item{object}{A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.}
 
 \item{subset_row}{A character vector (of feature names), a logical vector or numeric vector (of indices) specifying the features to use. The default of NULL will use all features.}
+
+\item{title}{Title of the component.}
+
+\item{cluster_rows}{A logical controls whether to make cluster on rows.}
+
+\item{cluster_columns}{A logical controls whether to make cluster on columns.}
+
+\item{clustering_method}{Method to perform hierarchical clustering, passed to \link[stats]{hclust}.}
+
+\item{clustering_distance}{The distance measure to use for hierarchical clustering.}
 }
 \value{
 A string containing markdown code for the rendered component
 
+An object of class \linkS4class{i2dash::i2dashboard}.
+
 An object of class \linkS4class{i2dash::i2dashboard}.
 }
 \description{
+Renders a component containing a heatmap based on \link[ComplexHeatmap]{Heatmap}.
+
 Renders a component containing a \link[ComplexHeatmap]{Heatmap}.
 }
diff --git a/man/multiplot.Rd b/man/multiplot.Rd
deleted file mode 100644
index 20af34caa436a639f337e0b658a5d13c6c610f28..0000000000000000000000000000000000000000
--- a/man/multiplot.Rd
+++ /dev/null
@@ -1,23 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/multiplot.R
-\name{multiplot}
-\alias{multiplot}
-\title{Renders a Sequence saturation plot}
-\usage{
-multiplot(object, plot_title, x, y, color_by)
-}
-\arguments{
-\item{plot_title}{The title of the Component}
-
-\item{x}{A list with the x-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.}
-
-\item{y}{A list with the y-axis values. If it is a nested list, a dropdown-field will be provided in the interactive mode.}
-
-\item{color_by}{A list with the color_by values. If it is a nested list, a dropdown-field will be provided in the interactive mode.}
-}
-\value{
-A string containing markdown code for the rendered textbox
-}
-\description{
-Renders a Sequence saturation plot
-}
diff --git a/man/plotly_barplot.Rd b/man/plotly_barplot.Rd
index 6ce3f56b0c19566ed6ed829e55183e2a5548473f..639da87a78c9e41a1c252b9c286f8c02f6acd81a 100644
--- a/man/plotly_barplot.Rd
+++ b/man/plotly_barplot.Rd
@@ -2,13 +2,13 @@
 % Please edit documentation in R/visualization_functions.R
 \name{plotly_barplot}
 \alias{plotly_barplot}
-\title{Render a bar plot with plotly.}
+\title{Render a barplot with plotly.}
 \usage{
 plotly_barplot(..., showlegend = NULL, x_group_by_title = NULL,
   y_group_by_title = NULL)
 }
 \arguments{
-\item{...}{these arguments are of either the form value or tag = value and should be valid for the 'plotly::plot_ly()' method.}
+\item{...}{these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.}
 
 \item{showlegend}{(Optional) Boolean value that describes if the legend should be shown.}
 
@@ -20,5 +20,5 @@ plotly_barplot(..., showlegend = NULL, x_group_by_title = NULL,
 An object of class \code{plotly}.
 }
 \description{
-Render a bar plot with plotly.
+Render a barplot with plotly.
 }
diff --git a/man/plotly_boxplot.Rd b/man/plotly_boxplot.Rd
index 0f4e54dff12b3e2a6195ef226ce82924043a8876..daba7dcadfaf79b1f3b8f262439110d8aa097160 100644
--- a/man/plotly_boxplot.Rd
+++ b/man/plotly_boxplot.Rd
@@ -5,7 +5,7 @@
 \title{Render a boxplot with plotly.}
 \usage{
 plotly_boxplot(x, group_by = NULL, x_title = NULL,
-  group_by_title = NULL)
+  group_by_title = NULL, ...)
 }
 \arguments{
 \item{x}{Numeric observations for the boxplot.}
@@ -15,6 +15,8 @@ plotly_boxplot(x, group_by = NULL, x_title = NULL,
 \item{x_title}{A title that describes the observations.}
 
 \item{group_by_title}{A title that describes the grouping factor.}
+
+\item{...}{these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.}
 }
 \value{
 An object of class \code{plotly}.
diff --git a/man/plotly_bubbleplot.Rd b/man/plotly_bubbleplot.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..e876258b4edba14f64becafca8198c566a86e256
--- /dev/null
+++ b/man/plotly_bubbleplot.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/visualization_functions.R
+\name{plotly_bubbleplot}
+\alias{plotly_bubbleplot}
+\title{Render a bubbleplot with plotly.}
+\usage{
+plotly_bubbleplot(x, y, size, x_title = NULL, y_title = NULL, ...)
+}
+\arguments{
+\item{x}{Numeric observations mapped to the x-axis.}
+
+\item{y}{Numeric observations mapped to the y-axis.}
+
+\item{size}{Numeric values defining the size of the dots.}
+
+\item{x_title}{The title of the x-axis.}
+
+\item{y_title}{The title of the y-axis.}
+
+\item{...}{these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.}
+}
+\value{
+An object of class \code{plotly}.
+}
+\description{
+Render a bubbleplot with plotly.
+}
diff --git a/man/plotly_scatterplot.Rd b/man/plotly_scatterplot.Rd
index 247db677ec8294e8ea3a9ad03d74999681dbecbf..880b3337058d1e39c059d3152ff740f6e7b068d4 100644
--- a/man/plotly_scatterplot.Rd
+++ b/man/plotly_scatterplot.Rd
@@ -7,15 +7,11 @@
 plotly_scatterplot(..., y_title = NULL, x_title = NULL)
 }
 \arguments{
-\item{df}{A dataframe containing the data for the boxplot}
+\item{...}{these arguments are of either the form value or tag = value and should be valid for the 'plotly::plot_ly()' method.}
 
-\item{labels}{A list with sample names, which should be of the same length as x and y.}
+\item{y_title}{The title of the x-axis.}
 
-\item{colour_by}{A list containing factorial values that will be used for colouring. In case of a named list, a dropdown menu will be provided in the interactive mode. Note: The length of all vectors in case of a named list should be of the same length as x and y.}
-
-\item{checkbox}{A boolean value as indicator for colouring by labels.}
-
-\item{selected_label}{The label (character) selected by the user.}
+\item{x_title}{The title of the y-axis.}
 }
 \value{
 An object of class \code{plotly}.
diff --git a/man/plotly_violinplot.Rd b/man/plotly_violinplot.Rd
index 64a8c0b8b356ab92bb39f757562fd8377f0440f2..17487557f42eebfd233181ca4f99eb3316bbb949 100644
--- a/man/plotly_violinplot.Rd
+++ b/man/plotly_violinplot.Rd
@@ -5,10 +5,7 @@
 \title{Render a vertical violin plot with plotly.}
 \usage{
 plotly_violinplot(y, group_by = NULL, y_title = NULL,
-  group_by_title = NULL)
-
-plotly_violinplot(y, group_by = NULL, y_title = NULL,
-  group_by_title = NULL)
+  group_by_title = NULL, ...)
 }
 \arguments{
 \item{y}{Numeric observations.}
@@ -19,21 +16,11 @@ plotly_violinplot(y, group_by = NULL, y_title = NULL,
 
 \item{group_by_title}{A title that describes the grouping factor.}
 
-\item{y}{Numeric observations.}
-
-\item{group_by}{A factor, by which observations can optionally be grouped.}
-
-\item{y_title}{A title that describes the observations.}
-
-\item{group_by_title}{A title that describes the grouping factor.}
+\item{...}{these arguments are of either the form \code{value} or \code{tag = value} and should be valid for the \code{plotly::plot_ly()} method.}
 }
 \value{
-An object of class \code{plotly}.
-
 An object of class \code{plotly}.
 }
 \description{
-Render a vertical violin plot with plotly.
-
 Render a vertical violin plot with plotly.
 }
diff --git a/man/scatterplot.Rd b/man/scatterplot.Rd
index 80edd72dd1045cc3ebbfc482a42d12964b0398d5..1853bfb0cbb15a494ed4a894da99136ab99cbfe2 100644
--- a/man/scatterplot.Rd
+++ b/man/scatterplot.Rd
@@ -5,6 +5,7 @@
 \alias{scatterplot}
 \alias{scatterplot,i2dashboard,missing-method}
 \alias{scatterplot,i2dashboard,SingleCellExperiment-method}
+\alias{scatterplot,i2dashboard,Seurat-method}
 \title{Renders a component containing a scatterplot with optional selection options}
 \usage{
 scatterplot(dashboard, object, ...)
@@ -14,23 +15,29 @@ scatterplot(dashboard, object, ...)
   title = NULL, x_title = NULL, y_title = NULL)
 
 \S4method{scatterplot}{i2dashboard,SingleCellExperiment}(dashboard, object,
-  use = "colData", x = NULL, y = NULL, colour_by = NULL,
-  reduced_dim = NULL, ...)
+  use = c("colData", "rowData", "reducedDim"), x = NULL, y = NULL,
+  colour_by = NULL, use_dimred = NULL, exprs_values = NULL,
+  subset_row = NULL, ...)
+
+\S4method{scatterplot}{i2dashboard,Seurat}(dashboard, object,
+  use = c("meta.data", "meta.features", "reduction"), x = NULL,
+  y = NULL, colour_by = NULL, use_dimred = NULL, assay = "RNA",
+  slot = NULL, subset_row = NULL, ...)
 }
 \arguments{
 \item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
 
-\item{object}{A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.}
+\item{object}{An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.}
 
-\item{x}{A data.frame (matrix) containing columns with numeric values that will be mapped to the x-axis.}
+\item{x}{Values that will be mapped to the x-axis (see Details).}
 
-\item{y}{A data.frame (matrix) containing columns with numeric values that will be mapped to the y-axis.}
+\item{y}{Values that will be mapped to the y-axis (see Details).}
 
-\item{colour_by}{An optional data.frame (matrix) containing columns with numeric or factorial values that will be used for colouring.}
+\item{colour_by}{Numeric or factorial values that will be used for colouring.}
 
 \item{labels}{An optional vector with sample names. A dropdown menu for colouring by label will be provided.}
 
-\item{exprs_values}{An optional data.frame (matrix) containing expression data of features of interest in rows and samples in columns.}
+\item{exprs_values}{Expression data of features of interest in rows and samples in columns (see Details).}
 
 \item{title}{The title of the components junk.}
 
@@ -38,15 +45,40 @@ scatterplot(dashboard, object, ...)
 
 \item{y_title}{An optional title of the y-axis. If not provided the column names from \code{y}  are used instead.}
 
-\item{use}{A character specifying where to obtain the data from. One of \code{"colData"}, \code{"rowData"}, \code{"reducedDim"}.}
+\item{use}{A character specifying where to obtain the data from \code{object} (see Details).}
+
+\item{use_dimred}{A character vector indicating the reduced dimension to use from \code{"object"} (see Details).}
+
+\item{assay}{A character defining the assay of \code{object} and is used for obtaining the \code{exprs_values} (default "RNA") (see Details).}
 
-\item{reducedDim}{A character vector indicating the reduced dimension to use from \code{"reducedDim"}}
+\item{slot}{A character defining the data slot of \code{assay}.}
 }
 \value{
 A string containing markdown code for the rendered component
 
+An object of class \linkS4class{i2dash::i2dashboard}.
+
 An object of class \linkS4class{i2dash::i2dashboard}.
 }
 \description{
 Renders a component containing a scatterplot with optional selection options
 }
+\details{
+The parameters \code{x}, \code{y}, \code{colour_by}, \code{use}, \code{use_dimred}, \code{exprs_values}, \code{assay} and \code{slot}) take different arguments depending on the class of \code{object}.
+  In case no object is supplied (\emph{i2dashboard,missing}-method), the parameters \code{x}, \code{y}, \code{colour_by} and \code{exprs_values} are expected to be of class \code{data.frame} or \code{matrix}. T he parameters \code{x}, \code{y} can also be a numeric vector. The parameters \code{use}, \code{use_dimred}, \code{assay} and \code{slot} can be ignored.
+  In case a \linkS4class{SingleCellExperiment::SingleCellExperiment} object is supplied, the parameters are expected to be of class \code{character}:
+  \itemize{
+    \item \code{use} "colData", "rowData", "reducedDim",
+    \item \code{use_dimred} the name of an item in \code{reducedDims(object)},
+    \item \code{exprs_values} a valid assay name from \code{assayNames(object)},
+    \item \code{colour_by} column names of \code{colData(object)} or \code{colData(object)} in dependence of \code{use}.
+  }
+  In case a \linkS4class{Seurat::Seurat} object is supplied, the parameters are expected to be of class \code{character}:
+  \itemize{
+    \item \code{use} "meta.data" for sample metadata, "meta.feature" for feature metadata, "reduction" for a dimension reduction,
+    \item \code{use_dimred} the name of an item in \code{object@reductions},
+    \item \code{assay} a valid assay name from \code{names(object@assays)},
+    \item \code{slot} a valid data slot from \code{assay},
+    \item \code{colour_by} column names of \code{use}.
+  }
+}
diff --git a/man/violinplot.Rd b/man/violinplot.Rd
index 1535782bc6c79cde1e82633b1a3c018cf194b6ed..9bb5dbd5c174021b294321f731d75c8e4c26eccb 100644
--- a/man/violinplot.Rd
+++ b/man/violinplot.Rd
@@ -5,6 +5,7 @@
 \alias{violinplot}
 \alias{violinplot,i2dashboard,missing-method}
 \alias{violinplot,i2dashboard,SingleCellExperiment-method}
+\alias{violinplot,i2dashboard,Seurat-method}
 \title{Renders a component containing a vertical violin plot}
 \usage{
 violinplot(dashboard, object, ...)
@@ -13,17 +14,20 @@ violinplot(dashboard, object, ...)
   title = NULL, y_title = NULL, group_by_title = NULL)
 
 \S4method{violinplot}{i2dashboard,SingleCellExperiment}(dashboard, object,
-  use = "colData", y = NULL, group_by = NULL, title = NULL,
-  y_title = NULL, group_by_title = NULL)
+  use = c("colData", "rowData"), y = NULL, group_by = NULL, ...)
+
+\S4method{violinplot}{i2dashboard,Seurat}(dashboard, object,
+  use = c("meta.data", "meta.features"), assay = "RNA", y = NULL,
+  group_by = NULL, ...)
 }
 \arguments{
 \item{dashboard}{An object of class \linkS4class{i2dash::i2dashboard}.}
 
-\item{object}{A valid \linkS4class{SingleCellExperiment::SingleCellExperiment} object.}
+\item{object}{An object of class \linkS4class{Seurat::Seurat} or \linkS4class{SingleCellExperiment::SingleCellExperiment}.}
 
-\item{y}{A data.frame (matrix) containing numeric observations for the vertical axis, or a character vector indicating column names of \code{colData(object)}, \code{rowData(object)}.}
+\item{y}{A data.frame (matrix) containing numeric observations for the vertical axisor a character vector indicating the columns to use from \code{use}.}
 
-\item{group_by}{An optional data.frame (matrix) with columns containing grouping factors for the horizontal axis.}
+\item{group_by}{An optional data.frame (matrix) with columns containing grouping factors for the horizontal axis or a character vector indicating the columns to use from \code{use}.}
 
 \item{title}{The title of the component.}
 
@@ -31,12 +35,12 @@ violinplot(dashboard, object, ...)
 
 \item{group_by_title}{The title of the x-axis.}
 
-\item{use}{A character specifying where to obtain the data from. One of \code{"colData"} or \code{"rowData"}.}
+\item{use}{A character specifying where to obtain the data from. Valid input for SingleCellExperiment object: ("colData", "rowData"). Valid input for Seurat object: ("meta.data" for sample metadata, "meta.feature" for feature metadata.)}
+
+\item{assay}{Necessery, if \code{use} = "meta.feature". A character defining the assay to obtain the feature metadata from (default "RNA").}
 }
 \value{
 A string containing markdown code for the rendered component
-
-An object of class \linkS4class{i2dash::i2dashboard}.
 }
 \description{
 Renders a component containing a vertical violin plot
diff --git a/vignettes/.install_extras b/vignettes/.install_extras
new file mode 100644
index 0000000000000000000000000000000000000000..642938a7d750ed0414317f4b04812f36f0d0880f
--- /dev/null
+++ b/vignettes/.install_extras
@@ -0,0 +1 @@
+^vignettes/images$
diff --git a/vignettes/developer_guide.Rmd b/vignettes/developer_guide.Rmd
new file mode 100644
index 0000000000000000000000000000000000000000..604842293ab9907098f2d2b80271be5a628ce0da
--- /dev/null
+++ b/vignettes/developer_guide.Rmd
@@ -0,0 +1,517 @@
+---
+title: "How to develope new components and linked pages"
+author:
+- name: Jens Preussner
+  affiliation: 
+  email: jens.preussner@mpi-bn.mpg.de
+- name: Arsenij Ustjanzew
+  affiliation: 
+  email: arsenij.ustjanzew@mpi-bn.mpg.de
+date: "`r BiocStyle::doc_date()`"
+package: "`r BiocStyle::pkg_ver('i2dash.scrnaseq')`"
+output: 
+  BiocStyle::html_document:
+    toc_float: true
+vignette: >
+  %\VignetteIndexEntry{2. How to develope new components and linked pages}
+  %\VignettePackage{i2dash.scrnaseq}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+**Compiled date**: `r Sys.Date()`
+
+**Last edited**: 23.09.2019
+
+**License**: `r packageDescription("i2dash.scrnaseq")[["LICENSE"]]`
+
+```{r style, echo = FALSE, results = 'asis'}
+  BiocStyle::markdown()
+```
+
+```{r setup, include = FALSE}
+knitr::opts_chunk$set(
+    collapse = TRUE,
+    comment = "#>",
+    error = FALSE,
+    warning = FALSE,
+    message = FALSE
+)
+stopifnot(requireNamespace("htmltools"))
+htmltools::tagList(rmarkdown::html_dependency_font_awesome())
+```
+
+# Background
+
+It is possible to create new components that can be used as regular i2dash.scrnaseq functions and extend the capabilities of your i2dashboard. This tutorial shows how to create a new component and how to implement shiny input and output widgets to provide interactive elements for the user. Moreover, it is explained how to create a new customized page and link plotly based charts together and datatables of `r BiocStyle::Biocpkg("DT")` with several plotly based charts.
+
+# Example of custom components
+
+This example demonstrates how to create a plotly based bubble chart as an i2dash component.
+
+## R function for the plot itself
+
+At first, we need to create a function with minimal plotly code for the bubbleplot. The `[...]` argument allow us to expand our possibilities to modify the plot. This code can be written in the file `R/visualization_functions.R`:
+
+```{r, eval=FALSE}
+#' Render a bubbleplot with plotly.
+#'
+#' @param x Numeric observations mapped to the x-axis.
+#' @param y Numeric observations mapped to the y-axis.
+#' @param size Numeric values defining the size of the dots.
+#' @param x_title The title of the x-axis.
+#' @param y_title The title of the y-axis.
+#' @param ... these arguments are of either the form value or tag = value and should be valid for the 'plotly::plot_ly()' method.
+#'
+#' @return An object of class \code{plotly}.
+#' @export
+plotly_bubbleplot <- function(x, y, size, x_title = NULL, y_title = NULL, ...){
+  plotly::plot_ly(x = x, y = y, size = size, type = 'scatter', mode = 'markers', marker = list( oparcity = 0.5), ...) %>%
+    plotly::layout(xaxis = list(title = x_title, showgrid = FALSE),
+                   yaxis = list(title = y_title, showgrid = FALSE)
+    )
+}
+```
+
+## R function for custom components
+
+Now we create a new file `R/bubbleplot.R` and write the function that evaluates the input and saves it in an .Rds file for this component. The input for this function:
+
+- **dashboard**: the i2dashboard object to add the component.
+- **x**, **y**, **size**, **colour_by**: to provide the interactivity these parameters accept data.frames. The columns are possible selection options in the shiny dropdown menues.
+- **labels**: should be a character vector. This argument is optional and will be displayed by mouse hover.
+- **title**, **x_title**, **y_title**: these parapeters are also optional and should be characters.
+
+```{r, eval=FALSE}
+bubbleplot <- function(dashboard, x, y, size, colour_by = NULL, labels = NULL, title = NULL, x_title = NULL, y_title = NULL) {
+ # see code below
+}
+```
+
+Inside the function, we need to validate the input: 
+
+```{r, eval=FALSE}
+# Validate input
+assertive.types::assert_is_any_of(x, c("data.frame", "matrix"))
+assertive.types::assert_is_any_of(y, c("data.frame", "matrix"))
+assertive.types::assert_is_any_of(size, c("data.frame", "matrix"))
+
+# select columns only containing numeric or integer values
+x %<>%
+  as.data.frame() %>%
+  dplyr::select_if(function(col) is.integer(col) | is.numeric(col))
+y %<>%
+  as.data.frame() %>%
+  dplyr::select_if(function(col) is.integer(col) | is.numeric(col))
+size %<>%
+  as.data.frame() %>%
+  dplyr::select_if(function(col) is.integer(col) | is.numeric(col))
+
+# provide column names
+if(is.null(colnames(x))) colnames(x) <- paste0("X_", 1:ncol(x))
+if(is.null(colnames(y))) colnames(y) <- paste0("Y_", 1:ncol(y))
+if(is.null(colnames(size))) colnames(size) <- paste0("Size_", 1:ncol(size))
+
+# check correct dimensions
+if(nrow(x) != nrow(y)) stop("The number of rows in 'x' and 'y' is not equal.")
+if(nrow(x) != nrow(size)) stop("The number of rows in 'x' and 'size' is not equal.")
+
+# check optional parameters
+if(!is.null(colour_by)){
+  assertive.types::assert_is_any_of(colour_by, c("data.frame", "matrix"))
+  colour_by %<>%
+    as.data.frame() %>%
+    dplyr::select_if(function(col) is.integer(col) | is.numeric(col) | is.factor(col))
+  if(is.null(colnames(colour_by))) colnames(colour_by) <- paste0("Color_", 1:ncol(colour_by))
+  if(nrow(x) != nrow(colour_by)) stop("The number of rows in 'x' and 'colour_by' is not equal.")
+}
+if(!is.null(labels)) assertive.types::assert_is_any_of(labels, "vector")
+if(!is.null(labels)) assertive.types::is_character(title)
+if(!is.null(labels)) assertive.types::is_character(x_title)
+if(!is.null(labels)) assertive.types::is_character(y_title)
+```
+
+Then the input is saved into an .Rds file. The `title` of the component as well as the `env_id` are not saved and will be provided as characters by knitting the template.
+
+```{r, eval=FALSE}
+# Create random env id
+env_id <- paste0("env_", stringi::stri_rand_strings(1, 6, pattern = "[A-Za-z0-9]"))
+
+# Create component environment
+env <- new.env()
+env$x <- x
+env$y <- y
+env$size <- size
+env$colour_by <- colour_by
+env$labels <- labels
+env$x_title <- x_title
+env$y_title <- y_title
+
+saveRDS(env, file = file.path(dashboard@datadir, paste0(env_id, ".rds")))
+```
+
+At the end of the function we return the rendered R markdown string of the component. 
+
+```{r, eval=FALSE}
+# Expand component
+timestamp <- Sys.time()
+expanded_component <- knitr::knit_expand(file = system.file("templates", "bubbleplot.Rmd", package = "i2dash.scrnaseq"), title = title, env_id = env_id, date = timestamp)
+return(expanded_component)
+```
+
+## R Markdown template
+
+In the next step, we create an R markdown template file of the bubbleplot component `inst/templates/bubbleplot.Rmd`. At the top of this document the `title` in curved brackets will be replaced by the provided title during knitting. Also the `env_id` will be replaced and the correct .rds file will be read in. `is_shiny` contains a logical whether the i2dashboard is interactive and should use shiny or not.
+
+```{r, echo = FALSE}
+r_chunk <- "```"
+htmltools::HTML(paste0(
+  "<pre><code>
+### {{ title }}
+
+",r_chunk,"{r}
+{{ env_id }} <- readRDS(file.path(datadir, '{{ env_id }}.rds'))
+is_shiny <- identical(knitr::opts_knit$get('rmarkdown.runtime'), 'shiny')
+library(magrittr)
+",r_chunk,"
+</code></pre>")
+)
+```
+
+
+### The static mode
+
+The following R code chunk is also in the same file `inst/templates/bubbleplot.Rmd`. This code chunk will be executed, if shiny is not used. 
+All variables we define in this code chunk that are not inside a function should be unique. This is made possible by using the env_id in the curved brackets. During the knitting process, `{{ env_id }}` will be replaced by the 'real' env_id and the variable names will be valid in the final R markdown string.
+
+First we set the variables for `x_title` and `y_title`. If the user had not provided his own titles, the names of the first column of `x` and `y` are used as the titles. If `colour_by` was provided the first column will be used as the input vector. In case of `labels`, the row names of `x` will be used, if `labels` was not provided by the user.
+
+Next, the plotly chart is created by the `i2dash.scrnaseq::plotly_bubbleplot()` function. In the static mode the first columns of the dataframes `x` and `y` are used as input vectors. Finally a dataframe is created, that will be then provided as download.
+
+```{r, echo = FALSE}
+r_chunk <- "```"
+htmltools::HTML(paste0(
+  "<pre>
+",r_chunk,"{r, eval=!is_shiny}
+# set variables 
+# the first column is always used
+if(!is.null({{ env_id }}$x_title)) x_title_{{ env_id }} <- {{ env_id }}$x_title else x_title_{{ env_id }} <- colnames({{ env_id }}$x)[1]
+if(!is.null({{ env_id }}$y_title)) y_title_{{ env_id }} <- {{ env_id }}$y_title else y_title_{{ env_id }} <- colnames({{ env_id }}$y)[1]
+if(!is.null({{ env_id }}$colour_by)) colour_by_{{ env_id }} <- {{ env_id }}$colour_by[,1] else colour_by_{{ env_id }} <- NULL
+if(!is.null({{ env_id }}$labels)) labels_{{ env_id }} <- {{ env_id }}$labels else labels_{{ env_id }} <- rownames({{ env_id }}$x)
+
+# creating the plot object
+plot_{{ env_id }} <- i2dash.scrnaseq::plotly_bubbleplot(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], size = {{ env_id }}$size[,1], color = colour_by_{{ env_id }}, text = labels_{{ env_id }}, y_title = y_title_{{ env_id }}, x_title = x_title_{{ env_id }})
+
+# Provide data for download
+if(is.null({{ env_id }}$colour_by)){
+  df_{{ env_id }} <- data.frame(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], size = {{ env_id }}$size[,1])
+} else {
+  df_{{ env_id }} <- data.frame(x = {{ env_id }}$x[,1], y = {{ env_id }}$y[,1], size = {{ env_id }}$size[,1], colour_by = {{ env_id }}$colour_by[,1])
+}
+htmltools::div(style='display:block;float:left;width:100%;height:90%;',
+  htmltools::tags$button(i2dash::embed_var(df_{{ env_id }})), plot_{{ env_id }})
+",r_chunk,"
+</pre>")
+)
+```
+
+### The interactive mode
+
+By adding Shiny to a flexdashboard, we create a dashboard that enables the user to change underlying parameters and see the results immediately. In our case we provide the possibility to select the values that should be mapped to the x- and y-axis as well as the values, that should be used as size factor and colouring. (The user can select the names of the column that should be used through shiny input widgets.) In a flexdashboard the code for the UI as well as the server code can be used within the same code chunk.The webpage of flexdashboard provides a good description how to use Shiny with flexdashboard (see [here](https://rmarkdown.rstudio.com/flexdashboard/shiny.html)).
+
+First we create Shiny `selectInput()` widgets if the target dataframe has more than one column. Also a download button will be provided, so the user can download the data that is used for the created plot. We handle the inputs by creating a reactive expression for each dataframe. Inside the reactives it is checked, if the dataframe has more than one column. If this is the case, the input values from the `selectInput()` widgets should be used. Then a `downloadHandler()` creates a dataframe containing the currently selected x, y, size and colour values for the download. Plotly's function `renderPlotly()` outputs and renders a plotly object (generatet by the function `i2dash.scrnaseq::plotly_bubbleplot()`) within Shiny. Finally, the UI elements are stored in a dropdown menu created with the package [shinyWidgets](https://github.com/dreamRs/shinyWidgets).
+
+```{r, echo = FALSE}
+r_chunk <- "```"
+htmltools::HTML(paste0(
+  "<pre>
+",r_chunk,"{r, eval=is_shiny}
+#
+# shiny input widgets
+#
+ui_list <- list()
+
+# shiny input widget for x
+if (ncol({{ env_id }}$x) > 1){
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput('input_x_{{ env_id }}', label = 'Select data for x axis:',
+                                            choices = colnames({{ env_id }}$x)))
+}
+
+# shiny input widget for y
+if (ncol({{ env_id }}$y) > 1){
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput('input_y_{{ env_id }}', label = 'Select data for y axis:',
+                                            choices = colnames({{ env_id }}$y)))
+}
+
+# shiny input widget for size
+if (ncol({{ env_id }}$size) > 1){
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput('input_size_{{ env_id }}', label = 'Select data for the size factor:',
+                                            choices = colnames({{ env_id }}$size)))
+}
+
+# shiny input widget for colour_by
+if (!is.null({{ env_id }}$colour_by)){
+  if(ncol({{ env_id }}$colour_by) > 1)
+  ui_list <- rlist::list.append(ui_list,
+                                selectInput('input_colour_{{ env_id }}', label = 'Select metadata for colouring:',
+                                            choices = colnames({{ env_id }}$colour_by)))
+}
+
+#
+# shiny download button
+#
+ui_list <- rlist::list.append(ui_list, tags$div(tags$br(), downloadButton('downloadData_{{ env_id }}', 'Download data')))
+
+#
+# Handle inputs
+#
+x_{{ env_id }} <- shiny::reactive({
+  if(ncol({{ env_id }}$x) == 1){
+    data <- {{ env_id }}$x[[1]]
+    title <- colnames({{ env_id }}$x)[1]
+    return(list(data = data, title = title))
+  } else {
+    data <- {{ env_id }}$x[[input$input_x_{{ env_id }}]]
+    title <- input$input_x_{{ env_id }}
+    return(list(data = data, title = title))
+  }
+})
+
+y_{{ env_id }} <- shiny::reactive({
+  if(ncol({{ env_id }}$y) == 1){
+    data <- {{ env_id }}$y[[1]]
+    title <- colnames({{ env_id }}$y)[1]
+    return(list(data = data, title = title))
+  } else {
+    data <- {{ env_id }}$y[[input$input_y_{{ env_id }}]]
+    title <- input$input_y_{{ env_id }}
+    return(list(data = data, title = title))
+  }
+})
+
+size_{{ env_id }} <- shiny::reactive({
+  if(ncol({{ env_id }}$size) == 1){
+    return(data <- {{ env_id }}$size[[1]])
+  } else {
+    return({{ env_id }}$size[[input$input_size_{{ env_id }}]])
+  }
+})
+
+colour_{{ env_id }} <- shiny::reactive({
+  if(!is.null({{ env_id }}$colour_by)){
+    if(ncol({{ env_id }}$colour_by) == 1){
+      return({{ env_id }}$colour_by[[1]])
+    } else {
+      return({{ env_id }}$colour_by[[input$input_colour_{{ env_id }}]])
+    }
+  } else {
+    return(NULL)
+  }
+})
+
+#
+# Download data.frame
+#
+output$downloadData_{{ env_id }} <- downloadHandler(
+  filename =  paste('data-', Sys.Date(), '.csv', sep=''),
+  content = function(file) {
+    if(is.null(colour_{{ env_id }}()$colour)){
+      df <- data.frame(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, size = size_{{ env_id }}())
+    } else {
+      df <- data.frame(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, size = size_{{ env_id }}(), colour_by = colour_{{ env_id }}()$colour)
+    }
+    write.csv(df, file)
+  }
+)
+
+#
+# reactive for plot creation
+#
+output$plot_{{ env_id }} <- plotly::renderPlotly({
+  if(!is.null({{ env_id }}$y_title)) y_title <- {{ env_id }}$y_title else y_title <- y_{{ env_id }}()$title
+  if(!is.null({{ env_id }}$x_title)) x_title <- {{ env_id }}$x_title else x_title <- x_{{ env_id }}()$title
+  if(!is.null({{ env_id }}$labels)) labels <- {{ env_id }}$labels else labels <- rownames({{ env_id }}$x)
+
+  i2dash.scrnaseq::plotly_bubbleplot(x = x_{{ env_id }}()$data, y = y_{{ env_id }}()$data, size = size_{{ env_id }}(), color = colour_{{ env_id }}(), text = paste0('label: ',labels, '</br>size: ',size_{{ env_id }}()), y_title = y_title, x_title = x_title)
+  
+  
+})
+
+#
+# Layout of component
+#
+shiny::fillRow(flex = c(NA, 1),
+      shinyWidgets::dropdownButton(div(style='max-height: 350px; overflow-x: auto;',do.call(shiny::inputPanel, ui_list)),
+                       circle = TRUE, status = 'danger', icon = icon('gear'), width = '300px',
+                       tooltip = shinyWidgets::tooltipOptions(title = 'Click, to change plot settings:'))
+      ,
+      plotly::plotlyOutput('plot_{{ env_id }}', height = '100%')
+)
+",r_chunk,"</pre>")
+)
+```
+
+# Example of a custom page 
+
+In the following, it is explained how to design a custom flexdashboard page and use it as a template within i2dash. The creation of a customized page allows the developer to use his own layout and enables the possibility for linking components together. 
+
+The linking of components is based on the relation of the data used in the components (e.g. the data is organized in one dataframe and the components visualize different aspects of this dataframe). The linking mechanic contains several clicking and selecting events to highlight datapoints or recalculate the chart along the selected datapoints. 
+
+In order to accomplish this task, it is necessery to create an R function for validating and saving the input similar to the example above. The data necessary for **all** components of the custom page should be validated by this function.
+
+Furthermore it is necessary to create a second file containing the Rmd template. The Rmd document does not contain the typical YAML header with the render parameters. This header will be added to the final i2dashboard in the assembling process. The Rmd document rather contains the template code of several components. As described above each component can provide a static and interactive version. 
+
+R Plotly supports several possibilities for linking charts together. It is possible to link plotly charts at client-side, which makes it possible to use this interactivity without Shiny in the static mode. In this case it is possible to use plotly in combination with the package [Crosstalk](https://rstudio.github.io/crosstalk/) or manipulate the plots via JavaScript. In the chapter \@ref(plotlystatic) we demonstrate how to use the client-side linking based on a `SharedData` object from Crosstalk. Nevertheless, the capabilities of client-side linking are limited by data transformation. R Plotly also supports several click events with Shiny that we can use for server-side linking of components in the interactive mode. This is explained in the chapters \@ref(plotlyinteractive) and enables more complex datatransformations between the components.
+
+## Linking plotly based components
+
+### The static mode {#plotlystatic}
+
+[This resource](https://plotly-r.com/client-side-linking.html) provides a very detailed explanation of the client-side linking with R plotly. The example Rmd code below describes a very simple template for a page containing  a scatterplot and a barplot linked together. The R code chunk that loads the data and checks, if shiny should be used, is only present once. The `env_id` is identical in all components of the page. For demonstration purpose the `mtcars` dataset is used instead of loading the data from the rds file.
+The following page contains a two-columns layout. In the `scatterplot` code chunk we create a `SharedData` obejct with the function `plotly::highlight_key()`. The `plotly` object uses this `SharedData` object instead of a simple dataframe. `plotly::highlight('plotly_selected')` defines the selection mode. In this case, we want to enable the selection of datapoints. The `barplot` code chunk creates the barplot chart, which uses the same `SharedData` object. The use of the same graphical queries defined by the `SharedData` object, enables the linking between the two plots. In this examle we can select datapoints of the scatterplot, which results in the rerendering of the number of observations of the barplot. We can also select bars of the barplot and the datapoints that belong to the bars will be highlighted. 
+
+It is also possible to create custom event handlers with JavaScript as described [here](https://plotly-r.com/js-event-handlers.html) and use it in combination with plotly's JavaScript functions [Plotly.restyle](https://plot.ly/javascript/plotlyjs-function-reference/#plotlyrestyle) and [Plotly.relayout](https://plot.ly/javascript/plotlyjs-function-reference/#plotlyrelayout) to change another plotly chart. 
+
+https://plot.ly/javascript/plotlyjs-function-reference/#plotlyrestyle
+
+```{r, echo = FALSE}
+r_chunk <- "```"
+htmltools::HTML(paste0(
+  "<pre>
+
+Client-side linking with i2dash
+====================================================
+
+Column
+----------------------------------------------------
+
+### Custom scatterplot
+
+",r_chunk,"{r}
+#{{ env_id }} <- readRDS(file.path(datadir, '{{ env_id }}.rds'))
+
+# as an example: 
+{{ env_id }} <- mtcars
+
+is_shiny <- identical(knitr::opts_knit$get('rmarkdown.runtime'), 'shiny')
+library(magrittr)
+",r_chunk,"
+
+",r_chunk,"{r scatterplot, eval=is_shiny}
+{{ env_id }}$cyl <- as.factor(df$cyl)
+
+sd_{{ env_id }}<- plotly::highlight_key(data = {{ env_id }}, group = 'cyl')
+
+plotly::plot_ly(sd_{{ env_id }}, x = ~wt, y = ~mpg, text = ~cyl, mode = 'markers+text', textposition = 'top') %>%
+  plotly::highlight('plotly_selected') 
+
+",r_chunk,"
+
+Column
+----------------------------------------------------
+
+### Custom barplot
+
+",r_chunk,"{r barplot, eval=is_shiny}
+
+plotly::plot_ly(sd_{{ env_id }}, x = ~cyl, color = ~cyl) %>%
+  plotly::layout(
+    xaxis = list(title = 'Number of cylinder'),
+    yaxis = list(title = 'Number of observations'),
+    barmode = 'overlay',
+    showlegend = FALSE
+  ) %>%
+  plotly::highlight('plotly_selected')
+",r_chunk,"</pre>")
+)
+```
+
+```{r plotlystatic, echo=FALSE, out.width='100%', fig.cap="\\label{fig:plotlystatic} Custom page with linked components."}
+knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/plotly_linking_static.png", mustWork=TRUE))
+```
+
+### The interactive mode {#plotlyinteractive}
+
+The following example is straight forward and demonstrates, how we can expand the capabilities of the dashboard by using Shiny widgets. The linking is implemented through plotly's `event_data()`, which outputs the data of the selected/clicked or brushed points and defines the type of plotly event. With the `source` argument we provide an unique identifier to the scatterplot so we can adress the right plot in the ``event_data()` function. By using the `key` parameter, we define an unique identifier for for each row. In the `barplot` chunk the keys of the `event_data` are used to subset the dataframe according to the selected points and rerender the barplot.
+The linking of the barplot with the scatterplot require more complex code, which is not shown here. [This resource](https://plotly-r.com/linking-views-with-shiny.html) provides a very detailed explanation of the server-side linking with R plotly.
+
+An alternative solution would be to create a `SharedData` object in a reactive expression according to the Shiny input widget and use the Crosstalk mechanic to accomplish the task, similar to the example above. In more complex Shiny apps we do not recomend to use Shiny input widgets and reactives along with Crosstalk because the interference can cause unexpected behaviours.
+
+```{r, echo = FALSE}
+r_chunk <- "```"
+htmltools::HTML(paste0(
+  "<pre>
+  
+Server-side linking with i2dash
+====================================================
+
+Column
+----------------------------------------------------
+
+### Custom scatterplot
+
+",r_chunk,"{r}
+#{{ env_id }} <- readRDS(file.path(datadir, '{{ env_id }}.rds'))
+
+# as an example: 
+df <- mtcars
+cols <- c('cyl', 'vs', 'am', 'gear', 'carb')
+df[cols] <- lapply(df[cols], as.factor)
+
+is_shiny <- identical(knitr::opts_knit$get('rmarkdown.runtime'), 'shiny')
+library(magrittr)
+",r_chunk,"
+
+",r_chunk,"{r scatterplot, eval=is_shiny}
+# extract all factor columns
+factors <- df %>% dplyr::select_if(is.factor) %>% colnames()
+
+# shiny input widget for grouping
+df_input <- selectInput('grouping_df', label = 'Select column for grouping:', choices = factors)
+
+# Output
+output$scatterplot_df <- plotly::renderPlotly({
+  plotly::plot_ly(df, x = ~wt, y = ~mpg, text = df[[input$grouping_df]], mode = 'markers+text', textposition = 'top', source = 'scatterplot_df', key = rownames(df), type = 'scatter')
+})
+
+# Layout of component
+shiny::fillCol(flex = c(NA, NA),
+      df_input,
+      plotly::plotlyOutput('scatterplot_df', height = '100%')
+)
+",r_chunk,"
+
+Column
+----------------------------------------------------
+
+### Custom barplot
+
+",r_chunk,"{r barplot, eval=is_shiny}
+output$barplot_df <- plotly::renderPlotly({
+  # Subsetting the dataframe according to the selected data points
+  brush <- plotly::event_data('plotly_selected', source = 'scatterplot_df')$key
+  if (is.null(brush)){
+    data <- df
+  } else {
+    data <- df[brush,]
+  }
+  
+  # Rendering the barplot
+  plotly::plot_ly(data, x = data[[input$grouping_df]], color = data[[input$grouping_df]]) %>%
+    plotly::layout(
+      xaxis = list(title = names(df[input$grouping_df])),
+      yaxis = list(title = 'Number of observations'),
+      barmode = 'overlay',
+      showlegend = FALSE
+    )
+})
+
+plotly::plotlyOutput('barplot_df', height = '100%')
+",r_chunk,"</pre>")
+)
+```
+
diff --git a/vignettes/images/barplot.PNG b/vignettes/images/barplot.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..26acc3d65df9a5a2e1ee34163817674a3ed2ecc2
Binary files /dev/null and b/vignettes/images/barplot.PNG differ
diff --git a/vignettes/images/boxplot_colData.PNG b/vignettes/images/boxplot_colData.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..afd505a758b13b97e9e3306b27c39f6f1baf4fef
Binary files /dev/null and b/vignettes/images/boxplot_colData.PNG differ
diff --git a/vignettes/images/dimred_metadata.PNG b/vignettes/images/dimred_metadata.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..261c898fdcf196500f5545908e468f10d985e06e
Binary files /dev/null and b/vignettes/images/dimred_metadata.PNG differ
diff --git a/vignettes/images/feature_grid_page.PNG b/vignettes/images/feature_grid_page.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..ab13783e74a277a8175f96c658552d9ad6c72b98
Binary files /dev/null and b/vignettes/images/feature_grid_page.PNG differ
diff --git a/vignettes/images/gene_expression_page.PNG b/vignettes/images/gene_expression_page.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..b7d445b343c5b929d979bad3cc36f3681a37236e
Binary files /dev/null and b/vignettes/images/gene_expression_page.PNG differ
diff --git a/vignettes/images/heatmap_options.PNG b/vignettes/images/heatmap_options.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..904ad144b6080035d76ce13bcc1bd4a0385ee0a4
Binary files /dev/null and b/vignettes/images/heatmap_options.PNG differ
diff --git a/vignettes/images/heatmaps.PNG b/vignettes/images/heatmaps.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..4a7c23063303fecbc7853c8c3bff21416e3ee858
Binary files /dev/null and b/vignettes/images/heatmaps.PNG differ
diff --git a/vignettes/images/plotly_linking_static.PNG b/vignettes/images/plotly_linking_static.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..fd6c3d8f95fb90e5cfe0b27b5a0b9d670c4c9a3a
Binary files /dev/null and b/vignettes/images/plotly_linking_static.PNG differ
diff --git a/vignettes/images/red_dim_page.PNG b/vignettes/images/red_dim_page.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..2c5b2de261e286a3e3843ab0ca73568b49a1e4c4
Binary files /dev/null and b/vignettes/images/red_dim_page.PNG differ
diff --git a/vignettes/images/red_dim_page_selection.PNG b/vignettes/images/red_dim_page_selection.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..9e3bb5cb670f1e13b96aca4e784e7f9faaf88545
Binary files /dev/null and b/vignettes/images/red_dim_page_selection.PNG differ
diff --git a/vignettes/images/red_dim_page_silhuette.PNG b/vignettes/images/red_dim_page_silhuette.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..86aeda71a1bd3d376751e7b367ee9377f2a95d37
Binary files /dev/null and b/vignettes/images/red_dim_page_silhuette.PNG differ
diff --git a/vignettes/images/scatterplot_basic.PNG b/vignettes/images/scatterplot_basic.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..74cfa3777c66256805ff32aa17653e4162a556e4
Binary files /dev/null and b/vignettes/images/scatterplot_basic.PNG differ
diff --git a/vignettes/images/scatterplot_colData.PNG b/vignettes/images/scatterplot_colData.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..92da9e474d437eb8382c643a732b98d19059a8c4
Binary files /dev/null and b/vignettes/images/scatterplot_colData.PNG differ
diff --git a/vignettes/images/scatterplot_colour_by.PNG b/vignettes/images/scatterplot_colour_by.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..d1bc6ea97302f7192123d344b4841c170074ea3f
Binary files /dev/null and b/vignettes/images/scatterplot_colour_by.PNG differ
diff --git a/vignettes/images/scatterplot_colour_by_expression.PNG b/vignettes/images/scatterplot_colour_by_expression.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..0287874df6507fbf79f12b5bdf61d4fa6843c226
Binary files /dev/null and b/vignettes/images/scatterplot_colour_by_expression.PNG differ
diff --git a/vignettes/images/scatterplot_colour_by_label.PNG b/vignettes/images/scatterplot_colour_by_label.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..cb0255cadd3ade214e00e49db8654c558b162cb2
Binary files /dev/null and b/vignettes/images/scatterplot_colour_by_label.PNG differ
diff --git a/vignettes/images/scatterplot_custom_title.PNG b/vignettes/images/scatterplot_custom_title.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..2d3dec69df72257232ee88309b5eab696a8c2e60
Binary files /dev/null and b/vignettes/images/scatterplot_custom_title.PNG differ
diff --git a/vignettes/images/scatterplot_options.PNG b/vignettes/images/scatterplot_options.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..902fd78fe95f8f2b84dc716137cb0b2fe2b782b6
Binary files /dev/null and b/vignettes/images/scatterplot_options.PNG differ
diff --git a/vignettes/images/violinplot_colData.PNG b/vignettes/images/violinplot_colData.PNG
new file mode 100644
index 0000000000000000000000000000000000000000..b7cb52bcbca0291e60ff9693e8b6190be2295cce
Binary files /dev/null and b/vignettes/images/violinplot_colData.PNG differ
diff --git a/vignettes/scrnaseq_dashboard.Rmd b/vignettes/scrnaseq_dashboard.Rmd
new file mode 100644
index 0000000000000000000000000000000000000000..eac12c7f3ae30e507c9de08fe2d8e9d3ddb4ca3b
--- /dev/null
+++ b/vignettes/scrnaseq_dashboard.Rmd
@@ -0,0 +1,505 @@
+---
+title: "Creating a single cell RNA-Seq dashboard with i2dash.scrnaseq"
+author:
+- name: Jens Preussner
+  affiliation: 
+  email: jens.preussner@mpi-bn.mpg.de
+- name: Arsenij Ustjanzew
+  affiliation: 
+  email: arsenij.ustjanzew@mpi-bn.mpg.de
+date: "`r BiocStyle::doc_date()`"
+package: "`r BiocStyle::pkg_ver('i2dash.scrnaseq')`"
+output: 
+  BiocStyle::html_document:
+    toc_float: true
+vignette: >
+  %\VignetteIndexEntry{1. Creating a single cell RNA-Seq dashboard with i2dash.scrnaseq}
+  %\VignettePackage{i2dash.scrnaseq}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+**Compiled date**: `r Sys.Date()`
+
+**Last edited**: 23.09.2019
+
+**License**: `r packageDescription("i2dash.scrnaseq")[["LICENSE"]]`
+
+```{r style, echo = FALSE, results = 'asis'}
+  BiocStyle::markdown()
+```
+
+```{r setup, include = FALSE}
+knitr::opts_chunk$set(
+    collapse = TRUE,
+    comment = "#>",
+    error = FALSE,
+    warning = FALSE,
+    message = FALSE
+)
+stopifnot(requireNamespace("htmltools"))
+htmltools::tagList(rmarkdown::html_dependency_font_awesome())
+```
+
+
+# Introduction 
+
+The rapid development of Next-Generation Sequencing (NGS) technologies enables the investigation of the transcriptome of thousands of cells in a single experiment. Single-cell RNA sequencing (scRNA-seq) allows to identify rare cell types and populations, to uncover gene regulatory networks, and to track the trajectories of distinct cell lineages in development. The potential of this technology leds to the development of a range of analysis workflows and pipelines. The visualisation, presentation and a compfortable sharing of the analysis results is often not the focus of these pipelines even though it is an important part of the scientific work.
+
+The i2dash R package enables the flexible and customised creation of dashboards for presenting and sharing the analysis results and provides a simple implementation in the workflow of analysis pipelines. The visual evaluation of the data after specific steps in an analysis pipeline is especially necessery in Single-cell workflows to provide a good quality controll, data correction, feature selection and dimensionality reduction. i2dash allows the assembly of the dashboard at each timepoint after new content is added to the i2dashboard object and thus the opportunity to check the visualized results after the keysteps. With the Shiny widgets and outputs you can provide controlled options for an efficient data exploration to the end-user of your dashboards.
+
+```{r, eval = FALSE}
+# For more details see the introduction vignette of i2dash (../doc/basic.html): 
+vignette("An introduction to i2dash", package = "i2dash")
+```
+  
+The i2dash.scrnaseq R package is an extension for the core i2dash package, which supplies several plotting methods embedded in components and pre-defined pages with linked components. The components provide different shiny based input possibilities (e.g. drop down selection boxes for changing the values plotted along the axes) and enables an enhanced user interactivity. All of the provided functions in the i2dash.scrnaseq package have wrapper for `r BiocStyle::Biocpkg("SingleCellExperiment")` and `r BiocStyle::Biocpkg("Seurat")` objects and enable the direct loading of these objects (even though it is worth mentioning that the loading speed of whole single cell objects is significantly slower).
+
+# Exploring the i2dash.scrnaseq capabilities
+
+## Setting up the data
+
+Throughout this vignette, we'll use the aztekin2019identification dataset from [scRNAseq](http://bioconductor.org/packages/devel/data/experiment/html/scRNAseq.html) which contains expression values for 13199 cells from Xenopus tail. With the `r BiocStyle::Biocpkg("scater")` package we add some metadata to the SingleCellExperiment object.
+
+```{r allen-dataset}
+# LTLA/scRNAseq: Collection of Public Single-Cell RNA-Seq Datasets
+# install.packages("remotes")
+# remotes::install_github("LTLA/scRNAseq")
+
+library(scRNAseq)
+library(SingleCellExperiment)
+library(scater)
+
+sce <- AztekinTailData()
+
+# Convert some columns to factors
+cols <- c("sample", "DevelopmentalStage", "DaysPostAmputation", "cluster", "CellCyclePhase", "Lane", "Condition", "batch")
+colData(sce)[cols] <- lapply(colData(sce)[cols], as.factor)
+
+# Normalize the expression values
+sce <- logNormCounts(sce, exprs_values="counts")
+
+# Calculating QC metrics
+sce <- calculateQCMetrics(sce)
+sce <- addQCPerCell(sce)
+sce <- addQCPerFeature(sce)
+```
+
+## Creating an i2dashboard object
+
+At first, we create a new i2dashboard object: 
+
+```{r}
+library(i2dash)
+library(i2dash.scrnaseq)
+
+dashboard <- new(
+  "i2dashboard",
+  title = "scRNA-Seq Analysis",
+  author = "i2dash team",
+  interactive = TRUE,
+  theme = "yeti",
+  datadir = "/datadir",
+  file = "MyDashboard.Rmd"
+)
+```
+
+## Components overview
+
+In the following, the provided componenets from i2dash.scrnaseq will be introduced.
+
+### Scatterplot
+
+For the demonstration, we add a new page "Scatterplot" with the default layout to the dashboard:
+
+```{r}
+dashboard <- i2dash::add_page(dashboard, page = "scatter", title = "Scatterplot", layout = "default")
+```
+
+For adding a scatterplot component to the i2dashboard object by using the SingleCellExperiment or Seurat object as input, the i2dash function `add_component()` is used. We specify the type of the component by providing the character string "i2dash.scrnaseq::scatterplot" as the `component` argument. With the `page` parameter we define the page the component is added to. If we do not define a page the component is added to the "default" page of the i2dashboard object. The SingleCellExperiment or Seurat object is provided by the parameter `object`. Additionally, we need to specify with `use` where the data for the scatterplot is obtained from. In case of a SingleCellExperiment object we can decide, wether the columns from "colData", "rowData" or "reducedDim" are used for mapping the x- and y-coordinates as well as for the colour by options. When we select "reducedDim" for the mapping, "colData" is used for the colour by option. By default all numeric and integer columns are utilized for the x and y-values and all numeric, integer or factorial columns for the colour by option. In the interactive mode dropdown menues for the x-, y-axis and colour_by are provided where the user can select the column that should be plotted (the column names are used in the dropdown menu). In the static mode always the first column is used. Furthermore, we can specify the columns that should be mapped. It should be a character vector containing the column names. Optionally, we can provide our own axis titles with the parameters `x_title` and `y_title`, that do not change according to the selected columns.
+
+```{r, eval=FALSE}
+# this component is added to the page "scatter" in the i2dashboard object;
+# by default the colData of the object is used for the 'use' parameter;
+# as x-values all valid columns are used; 
+# for y-values only the specified columns are used, because we provide a character vector; 
+
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::scatterplot",
+  page = "scatter",
+  object = sce,
+  y = c("total_counts", "log10_total_counts"), 
+  title = "sce scatterplot of colData" # title of the component
+)
+
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::scatterplot",
+  page = "scatter",
+  object = sce,
+  use = "reducedDim",
+  title = "sce scatterplot of reducedDim"
+)
+```
+
+Tipp: In static mode there are no selection options for the columns, therefore it is required to specify the columns which should be used with `x` and `y` parameters.
+
+```{r scatterplotOptions, echo=FALSE, out.width='65%', fig.cap="\\label{fig:scatterplotOptions} Example of selection options for the scatterplot in the interactive mode."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "../vignettes/images/scatterplot_options.png", mustWork=TRUE))
+knitr::include_graphics(path = system.file("images/scatterplot_options.PNG", package="i2dash.scrnaseq", mustWork=TRUE))
+```
+
+```{r scatterplotColData, echo=FALSE, out.width='100%', fig.cap="\\label{fig:scatterplotColData} Example of a scatterplot."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/scatterplot_colData.png", mustWork=TRUE))
+```
+
+### Violinplot
+
+With "i2dash.scrnaseq::violinplot" a component containing a vertical violinplot is created. The parameter `use` defines where to obtain the data from ("colData" or "rowData" in case of a SingleCellExperiment object). `y` describes the observations, optionally the observations can be grouped by factors(`group_by` argument). Therefore, we need to specify the columns containing factors that should be used for grouping the observations along the x-axis. By default all columns with numeric or integer values are used for the observations-dropdown menu in the interactive mode and no columns are used for grouping if "group_by" is not provided. Optionally, we can provide our own axis titles with the parameters `group_by_title` and `y_title`, that do not change according to the selected columns.
+
+
+```{r, eval=FALSE}
+dashboard <- i2dash::add_page(dashboard, page = "violin", title = "Violin plot", layout = "default")
+
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::violinplot",
+  page = "violin",
+  object = sce,
+  use = "colData",
+  group_by = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  title = "sce violin plot of colData"
+)
+```
+
+```{r violinplotColData, echo=FALSE, out.width='100%', fig.cap="\\label{fig:violinplotColData} Example of a violinplot."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/violinplot_colData.png", mustWork=TRUE))
+```
+
+### Boxplot
+
+With "i2dash.scrnaseq::boxplot" a component containing a horizontal boxplot is created. The parameter `use` defines where to obtain the data from ("colData" or "rowData" in case of a SingleCellExperiment object). `x` describes the observations, optionally the observations can be grouped by factors (`group_by` argument).  Therefore, we need to specify the columns containing factors that should be used for grouping the observations along the y-axis. By default all columns with numeric or integer values are used for the observations-dropdown menu in the interactive mode and no columns are used for grouping if "group_by" is not provided. Optionally, we can provide our own axis titles with the parameters `group_by_title` and `x_title`, that do not change according to the selected columns.
+
+```{r, eval=FALSE}
+dashboard <- i2dash::add_page(dashboard, page = "box", title = "Boxplot", layout = "default")
+
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::boxplot",
+  page = "box",
+  object = sce,
+  use = "colData",
+  group_by = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  title = "sce boxplot of colData"
+)
+```
+
+```{r boxplotColData, echo=FALSE, out.width='100%', fig.cap="\\label{fig:boxplotColData} Example of a boxplot."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/boxplot_colData.png", mustWork=TRUE))
+```
+
+### Barplot
+
+The barplot component created by "i2dash.scrnaseq::barplot" has two possible visualisation options. The first option visualizes a horizontal barplot with the number of observations along the x-axis that are grouped along the y-axis. This option is initiated by only providing the argument `y_group_by`. The second option generates a horizontal barplot with the relative number of observations grouped according to the levels provided by `x_group_by`. This option requires the provision of both parameters `y_group_by` (grouping the observations along the y-axis) and `x_group_by` (grouping the observations along the x-axis). Only columns containing factors are valid for this barplot component. By default, without a character vector provided for `y_group_by` contains the names of columns that should be used, all valid columns are utilized. No columns are used for `x_group_by`, if not provided. Again we need to specify where to obtain the data from with the `use` parameter. Optionally, we can provide our own axis titles with the parameters `y_group_by_title` and `x_group_by_title`, that do not change according to the selected columns.
+
+```{r, eval=FALSE}
+dashboard <- i2dash::add_page(dashboard, page = "bar", title = "Barplot", layout = "2x2_grid")
+
+# 1. mode: count number of observations
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::barplot",
+  page = "bar",
+  object = sce,
+  use = "colData",
+  title = "sce barplot: total number of observations",
+  x_group_by_title = "Number of cells"
+)
+
+# 2. mode: relative number of observations grouped by 'x_group_by'
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::barplot",
+  page = "bar",
+  object = sce,
+  y_group_by = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  x_group_by = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  title = "sce barplot: relative number of observations",
+  x_group_by_title = "Fraction of cells"
+)
+```
+
+```{r barplot, echo=FALSE, out.width='100%', fig.cap="\\label{fig:barplot} Example of the two visualization options of the barplot component."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/barplot.png", mustWork=TRUE))
+```
+
+### Heatmap
+
+i2dash.scrnaseq provides a component containing a heatmap based on the package ComplexHeatmap. In case of the wrapper for the SingleCellExperiment and Seurat object it is required to provide a vector with names of features/ genes of interest that match with the row names of the used assay (`subset_row`). The assay name is defined by `exprs_values`/ `assay` (by default the "counts" assay of the SingleCellExperiment object is used and the "data" slot of the "RNA" assay of the Seurat object). The rows of the heatmap are the features of interest and columns are the cells. Optionally, we can provide the parameter `column_split`, that should be a character vector containing the column names of "colData" (only columns with factors are valid). If provided, it enables two visualisation options. The first option creates a heatmap that is splitted according to the levels of `column_split`. The second option visualizes a heatmap where the cells (columns) are grouped by the levels of `column_split` (each level is a column). The selection of the visualization mode is provided in the interactive mode. Also the interactive mode enables the selection of the clustering distance, the clustering method, the options whether to cluster none, only the rows or columns or both. The static mode doesn't offer interactive selection options. Therefore we can specify these factors with the parameters `visualisation_mode` ("splitted" or "summarized" heatmap; `column_split` should be provided), `cluster_rows` (a logical), `cluster_columns` (a logical), `clustering_distance` (a pre-defined character which is one of the follwing: "euclidean", "maximum", "manhattan", "binary", "minkowski") and `clustering_method` (to perform hierarchical clustering ("average", "ward.D", "ward.D2", "single", "complete", "mcquitty", "median","centroid")).
+
+```{r, eval=FALSE}
+dashboard <- i2dash::add_page(dashboard, page = "heatmap", title = "Heatmap", layout = "2x2_grid")
+
+# the basic heatmap with cells as columns and features as rows
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::heatmap",
+  page = "heatmap",
+  object = sce,
+  features = rownames(sce)[1:20],
+  title = "basic heatmap sce"
+)
+
+# splitted heatmap
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::heatmap",
+  page = "heatmap",
+  object = sce,
+  features = rownames(sce)[1:20],
+  column_split = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  visualisation_mode = "splitted", # only relevant for static mode
+  cluster_rows = TRUE, # optional; only relevant for static mode
+  cluster_columns = FALSE, # optional; only relevant for static mode
+  title = "splitted heatmap"
+)
+
+# grouped/ summerazied heatmap
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::heatmap",
+  page = "heatmap",
+  object = sce,
+  features = rownames(sce)[1:20],
+  column_split = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  visualisation_mode = "summarized", # only relevant for static mode
+  cluster_rows = TRUE, # optional; only relevant for static mode
+  cluster_columns = TRUE, # optional; only relevant for static mode
+  title = "summarized heatmap"
+)
+```
+
+```{r heatmapOptions, echo=FALSE, out.width='65%', fig.cap="\\label{fig:heatmapOptions} Example of the options for a heatmap in the interactive mode."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/heatmap_options.png", mustWork=TRUE))
+```
+
+```{r heatmaps, echo=FALSE, out.width='100%', fig.cap="\\label{fig:heatmaps} Example of the three possible visualizations of the heatmap component."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/heatmaps.png", mustWork=TRUE))
+```
+
+## Pre-defined pages overview
+
+### Page of the feature expression
+
+The feature expression page (`add_feature_expression_page()`) renders a page with three linked components. The first component is a table representing the statistic of the number of cells per grouping factor. The second component is a scatterplot, showing samples in along coordinates from "reduced_dim" (SingleCellExperiment)/ "DimReduc" (Seurat) that is defined by `use_dimred`. With `metadata_columns` we provide a character vector containing the column names from "colData" of the SingleCellExperiment object (only columns with factors are used). The third component is a violin plot, that shows expression values from `exprs_values` by groups defined in `metadata_columns`.
+
+The components are linked together, which means that it is possible to select a row in the table, which represents a feature and the scatterplot is coloured by the expression values of the selected feature. Also it is possible to select several points in the scatterplot of the dimension reduction with the lasso or rectangular tool from plotly and the violinplot reacts to the selection and is recalculated. This functionality works also in the static mode.
+
+```{r, eval=FALSE}
+dashboard <- i2dash.scrnaseq::add_feature_expression_page(
+  dashboard = dashboard,
+  object = sce,
+  use_dimred = "UMAP",
+  exprs_values = "counts",
+  metadata_columns = c("CellCyclePhase","Lane","DaysPostAmputation","Condition"),
+  title = "Gene expression page",
+  features = rownames(sce)[1:50] # vector with features of interest
+)
+```
+
+```{r geneExpr, echo=FALSE, out.width='100%', fig.cap="\\label{fig:geneExpr} Example of the feature expression page with linked components."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/gene_expression_page.png", mustWork=TRUE))
+```
+
+
+### Page for dimension reduction and sample metadata analysis
+
+The dimension reduction page for sample metadata analysis (`add_dimred_sample_page()`) provides the possibility to inspect the dimension reduction and distribution of cell metadata (e.g. cluster, timepoint, number of genes, etc). This page with linked components is build up on the "2x2_grid" layout and contains one component of a dimension reduction and the three other component slots are occupied by either a box-, bar- or silhouette plot. `use_dimred` represents the name of a dimension reduction ("reducedDim" in SingleCellExperiment; "DimReduc" in Seurat). With `sample_metadata` we provide a character vector containing the column names from "colData"/ "meta.data". The data type of the metadata determines whether the component is a bar- (factors) or a boxplot (numeric). The parameter `group_by` should be one of the column names from `sample_metadata` and defines the colouring of the scatterplot. Optionally, one component slot will be filled with a silhouette plot, if the parameter `show_silhouette` is set to `TRUE`. The silhouette plot is grouped by `group_by`. Furthermore, if `show_group_sizes` is set to `TRUE`, a barplot with the number of observations from `group_by` will be created.
+
+The plots are linked together, which means that it is possible to select several points in the scatterplot of the dimension reduction with the lasso or rectangular tool from plotly and the other plots react to the selection and are recalculated. This functionality works also in the static mode.
+
+```{r, eval=FALSE}
+dashboard <- i2dash.scrnaseq::add_dimred_sample_page(
+  dashboard = dashboard,
+  object = sce,
+  use_dimred = "UMAP",
+  sample_metadata = c("CellCyclePhase","Lane","log10_total_counts","Condition"),
+  group_by = "Condition",
+  title = "Reduced dimension",
+  show_group_sizes = FALSE,
+  show_silhouette = FALSE
+)
+
+dashboard <- i2dash.scrnaseq::add_dimred_metadata_page(
+  dashboard = dashboard,
+  object = sce,
+  use_dimred = "UMAP",
+  sample_metadata = c("CellCyclePhase","Lane","log10_total_counts","Condition"),
+  group_by = "Condition",
+  title = "Reduced dimension",
+  show_group_sizes = TRUE,
+  show_silhouette = TRUE
+)
+```
+
+```{r dimred, echo=FALSE, out.width='100%', fig.cap="\\label{fig:dimred} Example of the dimension reduction page."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/red_dim_page.png", mustWork=TRUE))
+```
+
+```{r dimredSelection, echo=FALSE, out.width='100%', fig.cap="\\label{fig:dimredSelection} The linking functionality is shown."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/red_dim_page_selection.png", mustWork=TRUE))
+```
+
+```{r dimredSil, echo=FALSE, out.width='100%', fig.cap="\\label{fig:dimredSil} This dimension reduction page contains the silhuette plot and the grouped barplot."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/red_dim_page_silhuette.png", mustWork=TRUE))
+```
+
+### Page for dimension reduction and feature metadata analysis
+
+This page (`add_dimred_feature_page()`) can be used for the exploration of marker genes. The page contains two components, the first component is a scatterplot representing the dimension reduction (`use_dimred`) coloured by the expression values of the selected marker gene (from `exprs_values`). The second component is a table with feature metadata. This additional metadata is obtained from columns of "rowData"/ "feature.data". The rows represent the features. By clicking on a row the feature is selected in the table and the colouring of the scatterplot changes according to its exression values. This linking mechanic is available in the static mode.
+
+```{r, eval=FALSE}
+dashboard <- i2dash.scrnaseq::add_dimred_metadata_page(
+  dashboard = dashboard,
+  object = sce,
+  use_dimred = "UMAP",
+  exprs_values = "counts",
+  feature_metadata = c("mean_counts", "log10_mean_counts", "log10_mean_counts"), # from colnames(rowData(sce))
+  subset_row = rownames(sce)[1:50], # vector with features of interest
+  title = "Dimension Reduction & Metadata"
+)
+```
+
+```{r dimredMeta, echo=FALSE, out.width='100%', fig.cap="\\label{fig:dimredMeta} Example of the dimension reduction page with metadata table and linked components."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/dimred_metadata.png", mustWork=TRUE))
+```
+
+### Feature expression grid page
+
+The Feature expression grid page (`add_feature_grid_page()`) can be used only in the interactive mode. This page visualizes several feature expressions (obtained from `exprs_values`) along a dimension reduction in a grid layout. The user can select the dimension reduction (defined by the parameter `use_dimred`), the features of interest (`subset_row`) and the grid layout. If `subset_row` is not provided, all features of `exprs_values` are selectible on the page. After that, the user generates the image by clicking on a button. This page enables the user to create high-quality figures for presentations/ publications and define the dimensions of the figure, which is then provided for download.
+
+```{r, eval=FALSE}
+dashboard <- i2dash.scrnaseq::add_feature_grid_page(
+  dashboard = dashboard,
+  object = sce,
+  use_dimred = "UMAP",
+  exprs_values = "counts",
+  subset_row = rownames(sce)[1:100], # vector with features of interest
+  title = "Multi Gene Expression"
+)
+```
+
+```{r featuregrid, echo=FALSE, out.width='100%', fig.cap="\\label{fig:featuregrid} Example of the feature grid page."}
+#knitr::include_graphics(path = system.file(package="i2dash.scrnaseq", "/www/feature_grid_page.png", mustWork=TRUE))
+```
+
+## Usage without single-cell objects
+
+All components and pages can be used without a SingleCellExperiment or Seurat object. In case of the single-cell object a wrapper function is used, therefore it is possible to provide raw data (e.g. data.frames) to the underlying functions. The following is an example usecase of using the core function of the scatterplot component. For the illustration purpose we extract the data from the `sce` object:
+
+```{r, eval=FALSE}
+# Creating another page
+dashboard <- i2dash::add_page(dashboard, page = "core", title = "Core function scatterplot", layout = "default")
+
+# Extracting the data as data.frames
+x_df <- data.frame(
+  "total_counts" = colData(sce)$total_counts, 
+  "log10_total_counts" = colData(sce)$log10_total_counts
+)
+
+y_df <- data.frame(
+  "Features" = colData(sce)$total_features_by_counts, 
+  "log10_Features" = colData(sce)$log10_total_features_by_counts
+)
+
+colour_df <- data.frame(
+  "sample" = colData(sce)$sample,
+  "CellCyclePhase" = colData(sce)$CellCyclePhase,
+  "Lane" = colData(sce)$Lane,
+  "DaysPostAmputation" = colData(sce)$DaysPostAmputation,
+  "Condition" = colData(sce)$Condition
+)
+
+count_table <- as.matrix(counts(sce)[1:20,])
+
+# adding the scatterplot component to the i2dashboard object
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::scatterplot",
+  page = "core",
+  x = x_df,
+  y = y_df,
+  colour_by = colour_df,
+  labels = colnames(sce),
+  exprs_values = count_table,
+  title = "Using the core function of the scatterplot"
+)
+```
+
+```{r, eval=FALSE}
+# Creating another page
+dashboard <- i2dash::add_page(dashboard, page = "bubble", title = "Bubbleplot", layout = "default")
+
+# Extracting the data as data.frames
+x_df <- data.frame(
+  "total_counts" = colData(sce)$total_counts, 
+  "log10_total_counts" = colData(sce)$log10_total_counts
+)
+
+y_df <- data.frame(
+  "Features" = colData(sce)$total_features_by_counts, 
+  "log10_Features" = colData(sce)$log10_total_features_by_counts
+)
+
+size_df <- data.frame(
+  "total_counts" = colData(sce)$total_counts,
+  "CellCyclePhase" = colData(sce)$CellCyclePhase,
+  "sum" = colData(sce)$sum,
+  "DaysPostAmputation" = colData(sce)$DaysPostAmputation,
+  "Condition" = colData(sce)$Condition
+)
+
+colour_df <- data.frame(
+  "sample" = colData(sce)$sample,
+  "CellCyclePhase" = colData(sce)$CellCyclePhase,
+  "Lane" = colData(sce)$Lane,
+  "DaysPostAmputation" = colData(sce)$DaysPostAmputation,
+  "Condition" = colData(sce)$Condition
+)
+
+# adding the scatterplot component to the i2dashboard object
+dashboard <- i2dash::add_component(
+  dashboard = dashboard,
+  component = "i2dash.scrnaseq::bubbleplot",
+  page = "bubble",
+  x = x_df,
+  y = y_df,
+  size = size_df,
+  colour_by = colour_df,
+  labels = colnames(sce),
+  title = "Test of the bubbleplot"
+)
+```
+
+## Assemble the dashboard
+
+Finally, we assemble the dashboard:
+
+```{r, eval=FALSE}
+dashboard
+i2dash::assemble(
+  dashboard = dashboard,
+  file = "MyDashboard.Rmd"
+)
+```
+
+# Session Info {.unnumbered}
+
+```{r sessioninfo}
+sessionInfo()
+# devtools::session_info()
+```
+
+# References {.unnumbered}
+