diff --git a/dune/vtk/vtktimeserieswriter.hh b/dune/vtk/vtktimeserieswriter.hh
index 929d0fd174f94b5ab18bda5cd38d4e2313f2ebe0..32a3e467308fe4a4059e1ad5655e7c59f4d77d8f 100644
--- a/dune/vtk/vtktimeserieswriter.hh
+++ b/dune/vtk/vtktimeserieswriter.hh
@@ -56,7 +56,7 @@ namespace Dune
     bool initialized_ = false;
 
     // block size of attached data
-    std::vector<std::uint64_t> blocksize_;
+    std::vector<std::uint64_t> blocks_;
 
     std::string filenameMesh_;
     std::vector<std::pair<double, std::string>> timesteps_;
diff --git a/dune/vtk/vtktimeserieswriter.impl.hh b/dune/vtk/vtktimeserieswriter.impl.hh
index 224e80f8445cf59faf79b249fed25adaa9192712..015f65bd1be1be10c6443d5af0ec336c2ff5b5b2 100644
--- a/dune/vtk/vtktimeserieswriter.impl.hh
+++ b/dune/vtk/vtktimeserieswriter.impl.hh
@@ -36,34 +36,14 @@ void VtkTimeseriesWriter<W>
   if (!initialized_) {
     // write points and cells only once
     filenameMesh_ = p.string() + ".mesh.vtkdata";
-    std::ofstream file_mesh(filenameMesh_, std::ios_base::ate | std::ios::binary);
-
-    if (vtkWriter_.datatype_ == Vtk::FLOAT32)
-      blocksize_.push_back( vtkWriter_.template writePointsAppended<float>(file_mesh) );
-    else
-      blocksize_.push_back( vtkWriter_.template writePointsAppended<double>(file_mesh) );
-
-    auto bs = vtkWriter_.writeCellsAppended(file_mesh);
-    blocksize_.insert(blocksize_.end(), bs.begin(), bs.end());
+    std::ofstream out(filenameMesh_, std::ios_base::ate | std::ios::binary);
+    vtkWriter_.writeGridAppended(out, blocks_);
     initialized_ = true;
   }
 
   std::string filenameData = p.string() + "_t" + std::to_string(timesteps_.size()) + ".vtkdata";
-  std::ofstream file_data(filenameData, std::ios_base::ate | std::ios::binary);
-
-  for (auto const& v : vtkWriter_.pointData_) {
-    if (v.type() == Vtk::FLOAT32)
-      blocksize_.push_back( vtkWriter_.template writeDataAppended<float>(file_data, v, W::POINT_DATA) );
-    else
-      blocksize_.push_back( vtkWriter_.template writeDataAppended<double>(file_data, v, W::POINT_DATA) );
-  }
-  for (auto const& v : vtkWriter_.cellData_) {
-    if (v.type() == Vtk::FLOAT32)
-      blocksize_.push_back( vtkWriter_.template writeDataAppended<float>(file_data, v, W::CELL_DATA) );
-    else
-      blocksize_.push_back( vtkWriter_.template writeDataAppended<double>(file_data, v, W::CELL_DATA) );
-  }
-
+  std::ofstream out(filenameData, std::ios_base::ate | std::ios::binary);
+  vtkWriter_.writeDataAppended(out, blocks_);
   timesteps_.emplace_back(time, filenameData);
 }
 
@@ -80,7 +60,7 @@ void VtkTimeseriesWriter<W>
   p.remove_filename();
   p /= name.string();
   std::string filename = p.string() + ".ts." + vtkWriter_.getFileExtension();
-  vtkWriter_.writeTimeseriesFile(filename, filenameMesh_, timesteps_, blocksize_);
+  vtkWriter_.writeTimeseriesFile(filename, filenameMesh_, timesteps_, blocks_);
 
   // remove all temporary data files
   int ec = std::remove(filenameMesh_.c_str());
diff --git a/dune/vtk/vtkwriterinterface.hh b/dune/vtk/vtkwriterinterface.hh
index 221a96d4356e3a8b80a5d8211bead7979336015f..d5ae604fe58169ef75cbb228b2810e880da69bf6 100644
--- a/dune/vtk/vtkwriterinterface.hh
+++ b/dune/vtk/vtkwriterinterface.hh
@@ -87,35 +87,35 @@ namespace Dune
     /// Return the file extension of the serial file (not including the dot)
     virtual std::string fileExtension () const = 0;
 
+    /// Write points and cells in raw/compressed format to output stream
+    virtual void writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const = 0;
+
     // Write the point or cell values given by the grid function `fct` to the
     // output stream `out`. In case of binary format, stores the streampos of XML
     // attributes "offset" in the vector `offsets`.
     void writeData (std::ofstream& out,
                     std::vector<pos_type>& offsets,
                     VtkFunction const& fct,
-                    PositionTypes type) const;
+                    PositionTypes type,
+                    Std::optional<std::size_t> timestep = {}) const;
 
-    // Collect point or cell data (depending on \ref PositionTypes) and pass
-    // the resulting vector to \ref writeAppended.
-    template <class T>
-    std::uint64_t writeDataAppended (std::ofstream& out,
-                                     VtkFunction const& fct,
-                                     PositionTypes type) const;
+    // Write points-data and cell-data in raw/compressed format to output stream
+    void writeDataAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const;
 
     // Write the coordinates of the vertices to the output stream `out`. In case
     // of binary format, stores the streampos of XML attributes "offset" in the
     // vector `offsets`.
     void writePoints (std::ofstream& out,
-                      std::vector<pos_type>& offsets) const;
+                      std::vector<pos_type>& offsets,
+                      Std::optional<std::size_t> timestep = {}) const;
 
-    // Collect point positions and pass the resulting vector to \ref writeAppended.
-    template <class T>
-    std::uint64_t writePointsAppended (std::ofstream& out) const;
+    // Write Appended section and fillin offset values to XML attributes
+    void writeAppended (std::ofstream& out, std::vector<pos_type> const& offsets) const;
 
     // Write the `values` in blocks (possibly compressed) to the output
     // stream `out`. Return the written block size.
     template <class T>
-    std::uint64_t writeAppended (std::ofstream& out, std::vector<T> const& values) const;
+    std::uint64_t writeValuesAppended (std::ofstream& out, std::vector<T> const& values) const;
 
     /// Return PointData/CellData attributes for the name of the first scalar/vector/tensor DataArray
     std::string getNames (std::vector<VtkFunction> const& data) const;
diff --git a/dune/vtk/vtkwriterinterface.impl.hh b/dune/vtk/vtkwriterinterface.impl.hh
index d36dafc2502ea199572e20892df8eadb91e093f0..c5d4eccb6879bfd1cc46a09704841d2e94491dfa 100644
--- a/dune/vtk/vtkwriterinterface.impl.hh
+++ b/dune/vtk/vtkwriterinterface.impl.hh
@@ -52,12 +52,16 @@ void VtkWriterInterface<GV,DC>
 template <class GV, class DC>
 void VtkWriterInterface<GV,DC>
   ::writeData (std::ofstream& out, std::vector<pos_type>& offsets,
-               VtkFunction const& fct, PositionTypes type) const
+               VtkFunction const& fct, PositionTypes type,
+               Std::optional<std::size_t> timestep) const
 {
   out << "<DataArray Name=\"" << fct.name() << "\" type=\"" << to_string(fct.type()) << "\""
-      << " NumberOfComponents=\"" << fct.ncomps() << "\" format=\"" << (format_ == Vtk::ASCII ? "ascii\">\n" : "appended\"");
+      << " NumberOfComponents=\"" << fct.ncomps() << "\" format=\"" << (format_ == Vtk::ASCII ? "ascii\"" : "appended\"");
+  if (timestep)
+    out << " TimeStep=\"" << *timestep << "\"";
 
   if (format_ == Vtk::ASCII) {
+    out << ">\n";
     std::size_t i = 0;
     if (type == POINT_DATA) {
       auto data = dataCollector_.template pointData<double>(fct);
@@ -80,12 +84,16 @@ void VtkWriterInterface<GV,DC>
 
 template <class GV, class DC>
 void VtkWriterInterface<GV,DC>
-  ::writePoints (std::ofstream& out, std::vector<pos_type>& offsets) const
+  ::writePoints (std::ofstream& out, std::vector<pos_type>& offsets,
+                Std::optional<std::size_t> timestep) const
 {
   out << "<DataArray type=\"" << to_string(datatype_) << "\""
-      << " NumberOfComponents=\"3\" format=\"" << (format_ == Vtk::ASCII ? "ascii\">\n" : "appended\"");
+      << " NumberOfComponents=\"3\" format=\"" << (format_ == Vtk::ASCII ? "ascii\"" : "appended\"");
+  if (timestep)
+    out << " TimeStep=\"" << *timestep << "\"";
 
   if (format_ == Vtk::ASCII) {
+    out << ">\n";
     auto points = dataCollector_.template points<double>();
     std::size_t i = 0;
     for (auto const& v : points)
@@ -99,33 +107,44 @@ void VtkWriterInterface<GV,DC>
   }
 }
 
-
 template <class GV, class DC>
-  template <class T>
-std::uint64_t VtkWriterInterface<GV,DC>
-  ::writeDataAppended (std::ofstream& out, VtkFunction const& fct, PositionTypes type) const
+void VtkWriterInterface<GV,DC>
+  ::writeDataAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const
 {
-  assert(is_a(format_, Vtk::APPENDED) && "Function should by called only in appended mode!\n");
-
-  if (type == POINT_DATA) {
-    auto data = dataCollector_.template pointData<T>(fct);
-    return this->writeAppended(out, data);
-  } else {
-    auto data = dataCollector_.template cellData<T>(fct);
-    return this->writeAppended(out, data);
+  for (auto const& v : pointData_) {
+    blocks.push_back( v.type() == Vtk::FLOAT32
+      ? this->writeValuesAppended(out, dataCollector_.template pointData<float>(v))
+      : this->writeValuesAppended(out, dataCollector_.template pointData<double>(v)));
+  }
+  for (auto const& v : cellData_) {
+    blocks.push_back( v.type() == Vtk::FLOAT32
+      ? this->writeValuesAppended(out, dataCollector_.template cellData<float>(v))
+      : this->writeValuesAppended(out, dataCollector_.template cellData<double>(v)));
   }
 }
 
 
 template <class GV, class DC>
-  template <class T>
-std::uint64_t VtkWriterInterface<GV,DC>
-  ::writePointsAppended (std::ofstream& out) const
+void VtkWriterInterface<GV,DC>
+  ::writeAppended (std::ofstream& out, std::vector<pos_type> const& offsets) const
 {
-  assert(is_a(format_, Vtk::APPENDED) && "Function should by called only in appended mode!\n");
+  if (is_a(format_, Vtk::APPENDED)) {
+    out << "<AppendedData encoding=\"raw\">\n_";
+    std::vector<std::uint64_t> blocks;
+    writeGridAppended(out, blocks);
+    writeDataAppended(out, blocks);
+    out << "</AppendedData>\n";
+    pos_type appended_pos = out.tellp();
+
+    pos_type offset = 0;
+    for (std::size_t i = 0; i < offsets.size(); ++i) {
+      out.seekp(offsets[i]);
+      out << '"' << offset << '"';
+      offset += pos_type(blocks[i]);
+    }
 
-  auto points = dataCollector_.template points<T>();
-  return this->writeAppended(out, points);
+    out.seekp(appended_pos);
+  }
 }
 
 
@@ -173,7 +192,7 @@ std::uint64_t writeCompressed (unsigned char const* buffer, unsigned char* buffe
 template <class GV, class DC>
   template <class T>
 std::uint64_t VtkWriterInterface<GV,DC>
-  ::writeAppended (std::ofstream& out, std::vector<T> const& values) const
+  ::writeValuesAppended (std::ofstream& out, std::vector<T> const& values) const
 {
   assert(is_a(format_, Vtk::APPENDED) && "Function should by called only in appended mode!\n");
   pos_type begin_pos = out.tellp();
diff --git a/dune/vtk/writers/vtkimagedatawriter.hh b/dune/vtk/writers/vtkimagedatawriter.hh
index 0db3f86cddff7f42d6096ab0d074c6b43d574627..cc6f04e772562b96bc617e8d16a9b3da6c61ede4 100644
--- a/dune/vtk/writers/vtkimagedatawriter.hh
+++ b/dune/vtk/writers/vtkimagedatawriter.hh
@@ -49,6 +49,8 @@ namespace Dune
       return "vti";
     }
 
+    virtual void writeGridAppended (std::ofstream& /*out*/, std::vector<std::uint64_t>& /*blocks*/) const override {}
+
   private:
     using Super::dataCollector_;
     using Super::format_;
diff --git a/dune/vtk/writers/vtkimagedatawriter.impl.hh b/dune/vtk/writers/vtkimagedatawriter.impl.hh
index 235b4ef89186a2e3f772c9a4b3d4c7246111d2c6..b976b428d85686ca7ff1c69f40b429290d594ac8 100644
--- a/dune/vtk/writers/vtkimagedatawriter.impl.hh
+++ b/dune/vtk/writers/vtkimagedatawriter.impl.hh
@@ -67,37 +67,8 @@ void VtkImageDataWriter<GV,DC>
   out << "</Piece>\n";
   out << "</ImageData>\n";
 
-  std::vector<std::uint64_t> blocks; // size of i'th appended block
-  pos_type appended_pos = 0;
-  if (is_a(format_, Vtk::APPENDED)) {
-    out << "<AppendedData encoding=\"raw\">\n_";
-    appended_pos = out.tellp();
-    for (auto const& v : pointData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::POINT_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::POINT_DATA) );
-    }
-    for (auto const& v : cellData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::CELL_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::CELL_DATA) );
-    }
-    out << "</AppendedData>\n";
-  }
-
+  this->writeAppended(out, offsets);
   out << "</VTKFile>";
-
-  // fillin offset values and block sizes
-  if (is_a(format_, Vtk::APPENDED)) {
-    pos_type offset = 0;
-    for (std::size_t i = 0; i < offsets.size(); ++i) {
-      out.seekp(offsets[i]);
-      out << '"' << offset << '"';
-      offset += pos_type(blocks[i]);
-    }
-  }
 }
 
 
diff --git a/dune/vtk/writers/vtkrectilineargridwriter.hh b/dune/vtk/writers/vtkrectilineargridwriter.hh
index f7df76b54a5ce81e54b4fcfd215cb57f2f79a333..050e899bd9fc9dc3c2c9d518bce388c13b7586be 100644
--- a/dune/vtk/writers/vtkrectilineargridwriter.hh
+++ b/dune/vtk/writers/vtkrectilineargridwriter.hh
@@ -44,7 +44,8 @@ namespace Dune
     /// for [i] in [0,...,size).
     virtual void writeParallelFile (std::string const& pfilename, int size) const override;
 
-    void writeCoordinates (std::ofstream& out, std::vector<pos_type>& offsets) const;
+    void writeCoordinates (std::ofstream& out, std::vector<pos_type>& offsets,
+                           Std::optional<std::size_t> timestep = {}) const;
 
     template <class T>
     std::array<std::uint64_t, 3> writeCoordinatesAppended (std::ofstream& out) const;
@@ -54,6 +55,8 @@ namespace Dune
       return "vtr";
     }
 
+    virtual void writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const override;
+
   private:
     using Super::dataCollector_;
     using Super::format_;
diff --git a/dune/vtk/writers/vtkrectilineargridwriter.impl.hh b/dune/vtk/writers/vtkrectilineargridwriter.impl.hh
index 23b8f09a179abc27519e709217a687fcf8dab722..48a650ee3646ff6e7de7a23840fd560e4dfd180c 100644
--- a/dune/vtk/writers/vtkrectilineargridwriter.impl.hh
+++ b/dune/vtk/writers/vtkrectilineargridwriter.impl.hh
@@ -47,6 +47,11 @@ void VtkRectilinearGridWriter<GV,DC>
     out << "<Piece Extent=\"" << join(extent.begin(), extent.end()) << "\">\n";
   });
 
+  // Write point coordinates for x, y, and z ordinate
+  out << "<Coordinates>\n";
+  writeCoordinates(out, offsets);
+  out << "</Coordinates>\n";
+
   // Write data associated with grid points
   out << "<PointData" << this->getNames(pointData_) << ">\n";
   for (auto const& v : pointData_)
@@ -59,53 +64,11 @@ void VtkRectilinearGridWriter<GV,DC>
     this->writeData(out, offsets, v, Super::CELL_DATA);
   out << "</CellData>\n";
 
-  // Write point coordinates for x, y, and z ordinate
-  out << "<Coordinates>\n";
-  writeCoordinates(out, offsets);
-  out << "</Coordinates>\n";
-
   out << "</Piece>\n";
   out << "</RectilinearGrid>\n";
 
-  std::vector<std::uint64_t> blocks; // size of i'th appended block
-  pos_type appended_pos = 0;
-  if (is_a(format_, Vtk::APPENDED)) {
-    out << "<AppendedData encoding=\"raw\">\n_";
-    appended_pos = out.tellp();
-    for (auto const& v : pointData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::POINT_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::POINT_DATA) );
-    }
-    for (auto const& v : cellData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::CELL_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::CELL_DATA) );
-    }
-
-    if (datatype_ == Vtk::FLOAT32) {
-      auto bs = writeCoordinatesAppended<float>(out);
-      blocks.insert(blocks.end(), bs.begin(), bs.end());
-    } else {
-      auto bs = writeCoordinatesAppended<double>(out);
-      blocks.insert(blocks.end(), bs.begin(), bs.end());
-    }
-    out << "</AppendedData>\n";
-  }
-
+  this->writeAppended(out, offsets);
   out << "</VTKFile>";
-
-  // fillin offset values and block sizes
-  if (is_a(format_, Vtk::APPENDED)) {
-    pos_type offset = 0;
-    for (std::size_t i = 0; i < offsets.size(); ++i) {
-      out.seekp(offsets[i]);
-      out << '"' << offset << '"';
-      offset += pos_type(blocks[i]);
-    }
-  }
 }
 
 
@@ -131,6 +94,13 @@ void VtkRectilinearGridWriter<GV,DC>
       << " WholeExtent=\"" << join(wholeExtent.begin(), wholeExtent.end()) << "\""
       << ">\n";
 
+  // Write point coordinates for x, y, and z ordinate
+  out << "<PCoordinates>\n";
+  out << "<PDataArray Name=\"x\" type=\"" << to_string(datatype_) << "\" />\n";
+  out << "<PDataArray Name=\"y\" type=\"" << to_string(datatype_) << "\" />\n";
+  out << "<PDataArray Name=\"z\" type=\"" << to_string(datatype_) << "\" />\n";
+  out << "</PCoordinates>\n";
+
   // Write data associated with grid points
   out << "<PPointData" << this->getNames(pointData_) << ">\n";
   for (auto const& v : pointData_) {
@@ -153,13 +123,6 @@ void VtkRectilinearGridWriter<GV,DC>
   }
   out << "</PCellData>\n";
 
-  // Write point coordinates for x, y, and z ordinate
-  out << "<PCoordinates>\n";
-  out << "<PDataArray Name=\"x\" type=\"" << to_string(datatype_) << "\" />\n";
-  out << "<PDataArray Name=\"y\" type=\"" << to_string(datatype_) << "\" />\n";
-  out << "<PDataArray Name=\"z\" type=\"" << to_string(datatype_) << "\" />\n";
-  out << "</PCoordinates>\n";
-
   // Write piece file references
   dataCollector_.writePieces([&out,pfilename,ext=this->fileExtension()](int p, auto const& extent, bool write_extent)
   {
@@ -177,13 +140,17 @@ void VtkRectilinearGridWriter<GV,DC>
 
 template <class GV, class DC>
 void VtkRectilinearGridWriter<GV,DC>
-  ::writeCoordinates (std::ofstream& out, std::vector<pos_type>& offsets) const
+  ::writeCoordinates (std::ofstream& out, std::vector<pos_type>& offsets,
+                      Std::optional<std::size_t> timestep) const
 {
   std::string names = "xyz";
   if (format_ == Vtk::ASCII) {
     auto coordinates = dataCollector_.template coordinates<double>();
     for (std::size_t d = 0; d < 3; ++d) {
-      out << "<DataArray type=\"" << to_string(datatype_) << "\" Name=\"" << names[d] << "\" format=\"ascii\">\n";
+      out << "<DataArray type=\"" << to_string(datatype_) << "\" Name=\"" << names[d] << "\" format=\"ascii\"";
+      if (timestep)
+        out << " TimeStep=\"" << *timestep << "\"";
+      out << ">\n";
       std::size_t i = 0;
       for (auto const& c : coordinates[d])
         out << c << (++i % 6 != 0 ? ' ' : '\n');
@@ -193,6 +160,8 @@ void VtkRectilinearGridWriter<GV,DC>
   else { // Vtk::APPENDED format
     for (std::size_t j = 0; j < 3; ++j) {
       out << "<DataArray type=\"" << to_string(datatype_) << "\" Name=\"" << names[j] << "\" format=\"appended\"";
+      if (timestep)
+        out << " TimeStep=\"" << *timestep << "\"";
       out << " offset=";
       offsets.push_back(out.tellp());
       out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
@@ -203,20 +172,23 @@ void VtkRectilinearGridWriter<GV,DC>
 
 
 template <class GV, class DC>
-  template <class T>
-std::array<std::uint64_t,3> VtkRectilinearGridWriter<GV,DC>
-  ::writeCoordinatesAppended (std::ofstream& out) const
+void VtkRectilinearGridWriter<GV,DC>
+  ::writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const
 {
   assert(is_a(format_, Vtk::APPENDED) && "Function should by called only in appended mode!\n");
 
-  auto coordinates = dataCollector_.template coordinates<T>();
-
-  // write conncetivity, offsets, and types
-  std::uint64_t bs0 = this->writeAppended(out, coordinates[0]);
-  std::uint64_t bs1 = this->writeAppended(out, coordinates[1]);
-  std::uint64_t bs2 = this->writeAppended(out, coordinates[2]);
-
-  return {bs0, bs1, bs2};
+  // write coordinates along axis
+  if (datatype_ == Vtk::FLOAT32) {
+    auto coordinates = dataCollector_.template coordinates<float>();
+    blocks.push_back(this->writeValuesAppended(out, coordinates[0]));
+    blocks.push_back(this->writeValuesAppended(out, coordinates[1]));
+    blocks.push_back(this->writeValuesAppended(out, coordinates[2]));
+  } else {
+    auto coordinates = dataCollector_.template coordinates<double>();
+    blocks.push_back(this->writeValuesAppended(out, coordinates[0]));
+    blocks.push_back(this->writeValuesAppended(out, coordinates[1]));
+    blocks.push_back(this->writeValuesAppended(out, coordinates[2]));
+  }
 }
 
 } // end namespace Dune
diff --git a/dune/vtk/writers/vtkstructuredgridwriter.hh b/dune/vtk/writers/vtkstructuredgridwriter.hh
index b27d8901bf403b8173c9a7a7a20ac65271968bed..f6ebc6a5c2b70afe66f02ffbcffef8e1ebf2850c 100644
--- a/dune/vtk/writers/vtkstructuredgridwriter.hh
+++ b/dune/vtk/writers/vtkstructuredgridwriter.hh
@@ -49,6 +49,8 @@ namespace Dune
       return "vts";
     }
 
+    virtual void writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const override;
+
   private:
     using Super::dataCollector_;
     using Super::format_;
diff --git a/dune/vtk/writers/vtkstructuredgridwriter.impl.hh b/dune/vtk/writers/vtkstructuredgridwriter.impl.hh
index 61c69cb8f9936f9f784eb7b6e39283cf970a8b8b..1dcd7241622c62d7b0649dd828a31fa17bfe1c13 100644
--- a/dune/vtk/writers/vtkstructuredgridwriter.impl.hh
+++ b/dune/vtk/writers/vtkstructuredgridwriter.impl.hh
@@ -45,6 +45,11 @@ void VtkStructuredGridWriter<GV,DC>
     out << "<Piece Extent=\"" << join(extent.begin(), extent.end()) << "\">\n";
   });
 
+  // Write point coordinates
+  out << "<Points>\n";
+  this->writePoints(out, offsets);
+  out << "</Points>\n";
+
   // Write data associated with grid points
   out << "<PointData" << this->getNames(pointData_) << ">\n";
   for (auto const& v : pointData_)
@@ -57,50 +62,11 @@ void VtkStructuredGridWriter<GV,DC>
     this->writeData(out, offsets, v, Super::CELL_DATA);
   out << "</CellData>\n";
 
-  // Write point coordinates
-  out << "<Points>\n";
-  this->writePoints(out, offsets);
-  out << "</Points>\n";
-
   out << "</Piece>\n";
   out << "</StructuredGrid>\n";
 
-  std::vector<std::uint64_t> blocks; // size of i'th appended block
-  pos_type appended_pos = 0;
-  if (is_a(format_, Vtk::APPENDED)) {
-    out << "<AppendedData encoding=\"raw\">\n_";
-    appended_pos = out.tellp();
-    for (auto const& v : pointData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::POINT_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::POINT_DATA) );
-    }
-    for (auto const& v : cellData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::CELL_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::CELL_DATA) );
-    }
-
-    if (datatype_ == Vtk::FLOAT32)
-      blocks.push_back( this->template writePointsAppended<float>(out) );
-    else
-      blocks.push_back( this->template writePointsAppended<double>(out) );
-    out << "</AppendedData>\n";
-  }
-
+  this->writeAppended(out, offsets);
   out << "</VTKFile>";
-
-  // fillin offset values and block sizes
-  if (is_a(format_, Vtk::APPENDED)) {
-    pos_type offset = 0;
-    for (std::size_t i = 0; i < offsets.size(); ++i) {
-      out.seekp(offsets[i]);
-      out << '"' << offset << '"';
-      offset += pos_type(blocks[i]);
-    }
-  }
 }
 
 
@@ -126,6 +92,14 @@ void VtkStructuredGridWriter<GV,DC>
       << " WholeExtent=\"" << join(wholeExtent.begin(), wholeExtent.end()) << "\""
       << ">\n";
 
+  // Write points
+  out << "<PPoints>\n";
+  out << "<PDataArray"
+      << " type=\"" << to_string(datatype_) << "\""
+      << " NumberOfComponents=\"3\""
+      << " />\n";
+  out << "</PPoints>\n";
+
   // Write data associated with grid points
   out << "<PPointData" << this->getNames(pointData_) << ">\n";
   for (auto const& v : pointData_) {
@@ -148,14 +122,6 @@ void VtkStructuredGridWriter<GV,DC>
   }
   out << "</PCellData>\n";
 
-  // Write points
-  out << "<PPoints>\n";
-  out << "<PDataArray"
-      << " type=\"" << to_string(datatype_) << "\""
-      << " NumberOfComponents=\"3\""
-      << " />\n";
-  out << "</PPoints>\n";
-
   // Write piece file references
   dataCollector_.writePieces([&out,pfilename,ext=this->fileExtension()](int p, auto const& extent, bool write_extent)
   {
@@ -170,4 +136,21 @@ void VtkStructuredGridWriter<GV,DC>
   out << "</VTKFile>";
 }
 
+
+template <class GV, class DC>
+void VtkStructuredGridWriter<GV,DC>
+  ::writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const
+{
+  assert(is_a(format_, Vtk::APPENDED) && "Function should by called only in appended mode!\n");
+
+  // write points
+  if (datatype_ == Vtk::FLOAT32) {
+    auto points = dataCollector_.template points<float>();
+    blocks.push_back(this->writeValuesAppended(out, points));
+  } else {
+    auto points = dataCollector_.template points<double>();
+    blocks.push_back(this->writeValuesAppended(out, points));
+  }
+}
+
 } // end namespace Dune
diff --git a/dune/vtk/writers/vtkunstructuredgridwriter.hh b/dune/vtk/writers/vtkunstructuredgridwriter.hh
index f513923c3ad55de7e6a87f74f0633adfffc17350..f5d557c45ab1ab78d3a4cf8b55f953a176dc7d6d 100644
--- a/dune/vtk/writers/vtkunstructuredgridwriter.hh
+++ b/dune/vtk/writers/vtkunstructuredgridwriter.hh
@@ -57,15 +57,14 @@ namespace Dune
       return "vtu";
     }
 
+    virtual void writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const override;
+
     // Write the element connectivity to the output stream `out`. In case
     // of binary format, stores the streampos of XML attributes "offset" in the
     // vector `offsets`.
     void writeCells (std::ofstream& oust,
-                     std::vector<pos_type>& offsets) const;
-
-    // Collect element connectivity, offsets and element types, and pass the
-    // resulting vectors to \ref writeAppended.
-    std::array<std::uint64_t,3> writeCellsAppended (std::ofstream& out) const;
+                     std::vector<pos_type>& offsets,
+                     Std::optional<std::size_t> timestep = {}) const;
 
   private:
     using Super::dataCollector_;
diff --git a/dune/vtk/writers/vtkunstructuredgridwriter.impl.hh b/dune/vtk/writers/vtkunstructuredgridwriter.impl.hh
index 8488ad254174df366f53f4722fdd86cd5a641a1a..752e3b5bf42172dc80b6b5c1d6b8225d9cf080b9 100644
--- a/dune/vtk/writers/vtkunstructuredgridwriter.impl.hh
+++ b/dune/vtk/writers/vtkunstructuredgridwriter.impl.hh
@@ -45,6 +45,16 @@ void VtkUnstructuredGridWriter<GV,DC>
       << " NumberOfCells=\"" << dataCollector_.numCells() << "\""
       << ">\n";
 
+  // Write point coordinates
+  out << "<Points>\n";
+  this->writePoints(out, offsets);
+  out << "</Points>\n";
+
+  // Write element connectivity, types and offsets
+  out << "<Cells>\n";
+  writeCells(out, offsets);
+  out << "</Cells>\n";
+
   // Write data associated with grid points
   out << "<PointData" << this->getNames(pointData_) << ">\n";
   for (auto const& v : pointData_)
@@ -57,58 +67,11 @@ void VtkUnstructuredGridWriter<GV,DC>
     this->writeData(out, offsets, v, Super::CELL_DATA);
   out << "</CellData>\n";
 
-  // Write point coordinates
-  out << "<Points>\n";
-  this->writePoints(out, offsets);
-  out << "</Points>\n";
-
-  // Write element connectivity, types and offsets
-  out << "<Cells>\n";
-  writeCells(out, offsets);
-  out << "</Cells>\n";
-
   out << "</Piece>\n";
   out << "</UnstructuredGrid>\n";
 
-  std::vector<std::uint64_t> blocks; // size of i'th appended block
-  pos_type appended_pos = 0;
-  if (is_a(format_, Vtk::APPENDED)) {
-    out << "<AppendedData encoding=\"raw\">\n_";
-    appended_pos = out.tellp();
-    for (auto const& v : pointData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::POINT_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::POINT_DATA) );
-    }
-    for (auto const& v : cellData_) {
-      if (v.type() == Vtk::FLOAT32)
-        blocks.push_back( this->template writeDataAppended<float>(out, v, Super::CELL_DATA) );
-      else
-        blocks.push_back( this->template writeDataAppended<double>(out, v, Super::CELL_DATA) );
-    }
-
-    if (datatype_ == Vtk::FLOAT32)
-      blocks.push_back( this->template writePointsAppended<float>(out) );
-    else
-      blocks.push_back( this->template writePointsAppended<double>(out) );
-
-    auto bs = writeCellsAppended(out);
-    blocks.insert(blocks.end(), bs.begin(), bs.end());
-    out << "</AppendedData>\n";
-  }
-
+  this->writeAppended(out, offsets);
   out << "</VTKFile>";
-
-  // fillin offset values and block sizes
-  if (is_a(format_, Vtk::APPENDED)) {
-    pos_type offset = 0;
-    for (std::size_t i = 0; i < offsets.size(); ++i) {
-      out.seekp(offsets[i]);
-      out << '"' << offset << '"';
-      offset += pos_type(blocks[i]);
-    }
-  }
 }
 
 
@@ -130,6 +93,14 @@ void VtkUnstructuredGridWriter<GV,DC>
 
   out << "<PUnstructuredGrid GhostLevel=\"0\">\n";
 
+  // Write points
+  out << "<PPoints>\n";
+  out << "<PDataArray"
+      << " type=\"" << to_string(datatype_) << "\""
+      << " NumberOfComponents=\"3\""
+      << " />\n";
+  out << "</PPoints>\n";
+
   // Write data associated with grid points
   out << "<PPointData" << this->getNames(pointData_) << ">\n";
   for (auto const& v : pointData_) {
@@ -152,14 +123,6 @@ void VtkUnstructuredGridWriter<GV,DC>
   }
   out << "</PCellData>\n";
 
-  // Write points
-  out << "<PPoints>\n";
-  out << "<PDataArray"
-      << " type=\"" << to_string(datatype_) << "\""
-      << " NumberOfComponents=\"3\""
-      << " />\n";
-  out << "</PPoints>\n";
-
   // Write piece file references
   for (int p = 0; p < size; ++p) {
     std::string piece_source = pfilename + "_p" + std::to_string(p) + "." + this->fileExtension();
@@ -176,7 +139,7 @@ void VtkUnstructuredGridWriter<GV,DC>
   ::writeTimeseriesFile (std::string const& filename,
                          std::string const& filenameMesh,
                          std::vector<std::pair<double, std::string>> const& timesteps,
-                         std::vector<std::uint64_t> const& blocksize) const
+                         std::vector<std::uint64_t> const& blocks) const
 {
   std::ofstream out(filename, std::ios_base::ate | std::ios::binary);
   assert(out.is_open());
@@ -212,43 +175,14 @@ void VtkUnstructuredGridWriter<GV,DC>
 
   // Write point coordinates
   out << "<Points>\n";
-  for (std::size_t i = 0; i < timesteps.size(); ++i) {
-    out << "<DataArray"
-        << " type=\"" << to_string(datatype_) << "\""
-        << " NumberOfComponents=\"3\""
-        << " TimeStep=\"" << i << "\""
-        << " format=\"appended\""
-        << " offset=";
-    offsets[i].push_back(out.tellp());
-    out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
-    out << "/>\n";
-  }
+  for (std::size_t i = 0; i < timesteps.size(); ++i)
+    this->writePoints(out, offsets[i], i);
   out << "</Points>\n";
 
   // Write element connectivity, types and offsets
   out << "<Cells>\n";
-  for (std::size_t i = 0; i < timesteps.size(); ++i) {
-    out << "<DataArray type=\"Int64\" Name=\"connectivity\" format=\"appended\""
-        << " TimeStep=\"" << i << "\""
-        << " offset=";
-    offsets[i].push_back(out.tellp());
-    out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
-    out << "/>\n";
-
-    out << "<DataArray type=\"Int64\" Name=\"offsets\" format=\"appended\""
-        << " TimeStep=\"" << i << "\""
-        << " offset=";
-    offsets[i].push_back(out.tellp());
-    out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
-    out << "/>\n";
-
-    out << "<DataArray type=\"UInt8\" Name=\"types\" format=\"appended\""
-        << " TimeStep=\"" << i << "\""
-        << " offset=";
-    offsets[i].push_back(out.tellp());
-    out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
-    out << "/>\n";
-  }
+  for (std::size_t i = 0; i < timesteps.size(); ++i)
+    writeCells(out, offsets[i], i);
   out << "</Cells>\n";
 
   const std::size_t shift = offsets[0].size(); // number of blocks to write the grid
@@ -256,43 +190,22 @@ void VtkUnstructuredGridWriter<GV,DC>
   // Write data associated with grid points
   out << "<PointData" << this->getNames(pointData_) << ">\n";
   for (std::size_t i = 0; i < timesteps.size(); ++i) {
-    for (auto const& v : pointData_) {
-      out << "<DataArray"
-          << " Name=\"" << v.name() << "\""
-          << " TimeStep=\"" << i << "\""
-          << " type=\"" << to_string(datatype_) << "\""
-          << " NumberOfComponents=\"" << v.ncomps() << "\""
-          << " format=\"appended\""
-          << " offset=";
-      offsets[i].push_back(out.tellp());
-      out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
-      out << "/>\n";
-    }
+    for (auto const& v : pointData_)
+      this->writeData(out, offsets[i], v, Super::POINT_DATA, i);
   }
   out << "</PointData>\n";
 
   // Write data associated with grid cells
   out << "<CellData" << this->getNames(cellData_) << ">\n";
   for (std::size_t i = 0; i < timesteps.size(); ++i) {
-    for (auto const& v : cellData_) {
-      out << "<DataArray"
-          << " Name=\"" << v.name() << "\""
-          << " TimeStep=\"" << i << "\""
-          << " type=\"" << to_string(datatype_) << "\""
-          << " NumberOfComponents=\"" << v.ncomps() << "\""
-          << " format=\"appended\""
-          << " offset=";
-      offsets[i].push_back(out.tellp());
-      out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
-      out << "/>\n";
-    }
+    for (auto const& v : cellData_)
+      this->writeData(out, offsets[i], v, Super::CELL_DATA, i);
   }
   out << "</CellData>\n";
 
   out << "</Piece>\n";
   out << "</UnstructuredGrid>\n";
 
-  std::vector<std::uint64_t> blocks; // size of i'th appended block
   pos_type appended_pos = 0;
   out << "<AppendedData encoding=\"raw\">\n_";
   appended_pos = out.tellp();
@@ -300,7 +213,7 @@ void VtkUnstructuredGridWriter<GV,DC>
   std::ifstream file_mesh(filenameMesh, std::ios_base::in | std::ios_base::binary);
   out << file_mesh.rdbuf();
   file_mesh.close();
-  assert( std::uint64_t(out.tellp()) == std::accumulate(blocksize.begin(), std::next(blocksize.begin(),shift), std::uint64_t(appended_pos)) );
+  assert( std::uint64_t(out.tellp()) == std::accumulate(blocks.begin(), std::next(blocks.begin(),shift), std::uint64_t(appended_pos)) );
 
   for (auto const& timestep : timesteps) {
     std::ifstream file(timestep.second, std::ios_base::in | std::ios_base::binary);
@@ -320,7 +233,7 @@ void VtkUnstructuredGridWriter<GV,DC>
     for (std::size_t j = 0; j < shift; ++j) {
       out.seekp(off[j]);
       out << '"' << offset << '"';
-      offset += pos_type(blocksize[j]);
+      offset += pos_type(blocks[j]);
     }
   }
 
@@ -331,7 +244,7 @@ void VtkUnstructuredGridWriter<GV,DC>
     for (std::size_t k = shift; k < off.size(); ++k) {
       out.seekp(off[k]);
       out << '"' << offset << '"';
-      offset += pos_type(blocksize[j++]);
+      offset += pos_type(blocks[j++]);
     }
   }
 }
@@ -339,23 +252,33 @@ void VtkUnstructuredGridWriter<GV,DC>
 
 template <class GV, class DC>
 void VtkUnstructuredGridWriter<GV,DC>
-  ::writeCells (std::ofstream& out, std::vector<pos_type>& offsets) const
+  ::writeCells (std::ofstream& out, std::vector<pos_type>& offsets,
+                Std::optional<std::size_t> timestep) const
 {
   if (format_ == Vtk::ASCII) {
     auto cells = dataCollector_.cells();
-    out << "<DataArray type=\"Int64\" Name=\"connectivity\" format=\"ascii\">\n";
+    out << "<DataArray type=\"Int64\" Name=\"connectivity\" format=\"ascii\"";
+    if (timestep)
+      out << " TimeStep=\"" << *timestep << "\"";
+    out << ">\n";
     std::size_t i = 0;
     for (auto const& c : cells.connectivity)
       out << c << (++i % 6 != 0 ? ' ' : '\n');
     out << (i % 6 != 0 ? "\n" : "") << "</DataArray>\n";
 
-    out << "<DataArray type=\"Int64\" Name=\"offsets\" format=\"ascii\">\n";
+    out << "<DataArray type=\"Int64\" Name=\"offsets\" format=\"ascii\"";
+    if (timestep)
+      out << " TimeStep=\"" << *timestep << "\"";
+    out << ">\n";
     i = 0;
     for (auto const& o : cells.offsets)
       out << o << (++i % 6 != 0 ? ' ' : '\n');
     out << (i % 6 != 0 ? "\n" : "") << "</DataArray>\n";
 
-    out << "<DataArray type=\"UInt8\" Name=\"types\" format=\"ascii\">\n";
+    out << "<DataArray type=\"UInt8\" Name=\"types\" format=\"ascii\"";
+    if (timestep)
+      out << " TimeStep=\"" << *timestep << "\"";
+    out << ">\n";
     i = 0;
     for (auto const& t : cells.types)
       out << int(t) << (++i % 6 != 0 ? ' ' : '\n');
@@ -363,18 +286,24 @@ void VtkUnstructuredGridWriter<GV,DC>
   }
   else { // Vtk::APPENDED format
     out << "<DataArray type=\"Int64\" Name=\"connectivity\" format=\"appended\"";
+    if (timestep)
+      out << " TimeStep=\"" << *timestep << "\"";
     out << " offset=";
     offsets.push_back(out.tellp());
     out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
     out << "/>\n";
 
     out << "<DataArray type=\"Int64\" Name=\"offsets\" format=\"appended\"";
+    if (timestep)
+      out << " TimeStep=\"" << *timestep << "\"";
     out << " offset=";
     offsets.push_back(out.tellp());
     out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
     out << "/>\n";
 
     out << "<DataArray type=\"UInt8\" Name=\"types\" format=\"appended\"";
+    if (timestep)
+      out << " TimeStep=\"" << *timestep << "\"";
     out << " offset=";
     offsets.push_back(out.tellp());
     out << std::string(std::numeric_limits<std::uint64_t>::digits10 + 2, ' ');
@@ -384,20 +313,25 @@ void VtkUnstructuredGridWriter<GV,DC>
 
 
 template <class GV, class DC>
-std::array<std::uint64_t,3> VtkUnstructuredGridWriter<GV,DC>
-  ::writeCellsAppended (std::ofstream& out) const
+void VtkUnstructuredGridWriter<GV,DC>
+  ::writeGridAppended (std::ofstream& out, std::vector<std::uint64_t>& blocks) const
 {
   assert(is_a(format_, Vtk::APPENDED) && "Function should by called only in appended mode!\n");
 
-  auto cells = dataCollector_.cells();
+  // write points
+  if (datatype_ == Vtk::FLOAT32) {
+    auto points = dataCollector_.template points<float>();
+    blocks.push_back(this->writeValuesAppended(out, points));
+  } else {
+    auto points = dataCollector_.template points<double>();
+    blocks.push_back(this->writeValuesAppended(out, points));
+  }
 
   // write conncetivity, offsets, and types
-  std::uint64_t bs0 = this->writeAppended(out, cells.connectivity);
-  std::uint64_t bs1 = this->writeAppended(out, cells.offsets);
-  std::uint64_t bs2 = this->writeAppended(out, cells.types);
-
-  return {bs0, bs1, bs2};
+  auto cells = dataCollector_.cells();
+  blocks.push_back(this->writeValuesAppended(out, cells.connectivity));
+  blocks.push_back(this->writeValuesAppended(out, cells.offsets));
+  blocks.push_back(this->writeValuesAppended(out, cells.types));
 }
 
-
 } // end namespace Dune