diff --git a/src/main.cpp b/src/main.cpp
index 2aee459b7de4503a5f61dfab71922e3cacb21716..a0ab842a3ca1b0961ce64fddfeb1fb04d68cf714 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -120,7 +120,9 @@ int main(int argc, char *argv[])
           VTKWriter vtk_writer("mesh", 0.01);
 
           while((t<tmax) and (iteration<itermax)) {
-            vtk_writer.write(mesh, t);
+            vtk_writer.write(mesh, {NamedItemValue{"density", rhoj},
+                                    NamedItemValue{"velocity", unknowns.uj()},
+                                    NamedItemValue{"coords", mesh.xr()}}, t);
             double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
             if (t+dt>tmax) {
               dt=tmax-t;
@@ -132,7 +134,9 @@ int main(int argc, char *argv[])
             t += dt;
             ++iteration;
           }
-          vtk_writer.write(mesh, t, true); // forces last output
+          vtk_writer.write(mesh, {NamedItemValue{"density", rhoj},
+                                  NamedItemValue{"velocity", unknowns.uj()},
+                                  NamedItemValue{"coords", mesh.xr()}}, t, true); // forces last output
 
           pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
                     << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
@@ -227,7 +231,9 @@ int main(int argc, char *argv[])
           VTKWriter vtk_writer("mesh", 0.01);
 
           while((t<tmax) and (iteration<itermax)) {
-            vtk_writer.write(mesh, t);
+            vtk_writer.write(mesh, {NamedItemValue{"density", rhoj},
+                                    NamedItemValue{"velocity", unknowns.uj()},
+                                    NamedItemValue{"coords", mesh.xr()}}, t);
             double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
             if (t+dt>tmax) {
               dt=tmax-t;
@@ -239,7 +245,9 @@ int main(int argc, char *argv[])
             t += dt;
             ++iteration;
           }
-          vtk_writer.write(mesh, t, true); // forces last output
+          vtk_writer.write(mesh, {NamedItemValue{"density", rhoj},
+                                  NamedItemValue{"velocity", unknowns.uj()},
+                                  NamedItemValue{"coords", mesh.xr()}}, t, true); // forces last output
 
           pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
                     << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
@@ -323,7 +331,9 @@ int main(int argc, char *argv[])
           VTKWriter vtk_writer("mesh", 0.01);
 
           while((t<tmax) and (iteration<itermax)) {
-            vtk_writer.write(mesh, t);
+            vtk_writer.write(mesh, {NamedItemValue{"density", rhoj},
+                                    NamedItemValue{"velocity", unknowns.uj()},
+                                    NamedItemValue{"coords", mesh.xr()}}, t);
             double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
             if (t+dt>tmax) {
               dt=tmax-t;
@@ -334,7 +344,9 @@ int main(int argc, char *argv[])
             t += dt;
             ++iteration;
           }
-          vtk_writer.write(mesh, t, true); // forces last output
+          vtk_writer.write(mesh, {NamedItemValue{"density", rhoj},
+                                  NamedItemValue{"velocity", unknowns.uj()},
+                                  NamedItemValue{"coords", mesh.xr()}}, t, true); // forces last output
 
           pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
                     << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
diff --git a/src/mesh/ItemToItemMatrix.hpp b/src/mesh/ItemToItemMatrix.hpp
index 60195176262fc8b62d2be7cdbaf55c01af5573d5..bf7e86ebca7d279a03586a6ad183b24887457b23 100644
--- a/src/mesh/ItemToItemMatrix.hpp
+++ b/src/mesh/ItemToItemMatrix.hpp
@@ -62,6 +62,16 @@ class ItemToItemMatrix
   const ConnectivityMatrix& m_connectivity_matrix;
 
  public:
+  auto numberOfEntries() const
+  {
+    return m_connectivity_matrix.numEntries();
+  }
+
+  const auto& entries() const
+  {
+    return m_connectivity_matrix.entries();
+  }
+
   PASTIS_INLINE
   auto operator[](const SourceItemId& source_id) const
   {
diff --git a/src/output/OutputNamedItemValueSet.hpp b/src/output/OutputNamedItemValueSet.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4f056098840f338f6afd869acad83f2f567d7d2f
--- /dev/null
+++ b/src/output/OutputNamedItemValueSet.hpp
@@ -0,0 +1,121 @@
+#ifndef OUTPUT_NAMED_ITEM_VALUE_SET_HPP
+#define OUTPUT_NAMED_ITEM_VALUE_SET_HPP
+
+#include <ItemValue.hpp>
+#include <TinyVector.hpp>
+#include <TinyMatrix.hpp>
+
+#include <variant>
+#include <map>
+#include <string>
+
+template <typename DataType,
+          ItemType item_type>
+class NamedItemValue
+{
+ private:
+  std::string m_name;
+  ItemValue<const DataType,item_type> m_item_value;
+
+ public:
+  constexpr const std::string& name() const
+  {
+    return m_name;
+  }
+
+  constexpr const ItemValue<const DataType,item_type>& itemValue() const
+  {
+    return m_item_value;
+  }
+
+  NamedItemValue& operator=(const NamedItemValue&) = default;
+  NamedItemValue& operator=(NamedItemValue&&) = default;
+
+  NamedItemValue(const std::string& name, const ItemValue<DataType,item_type>& item_value)
+      : m_name(name),
+        m_item_value(item_value)
+  {
+    ;
+  }
+
+  NamedItemValue(const std::string& name, const ItemValue<const DataType,item_type>& item_value)
+      : m_name(name),
+        m_item_value(item_value)
+  {
+    ;
+  }
+
+  NamedItemValue(const NamedItemValue&) = default;
+  NamedItemValue(NamedItemValue&&) = default;
+  ~NamedItemValue() = default;
+};
+
+class OutputNamedItemValueSet
+{
+ public:
+  using ItemValueVariant = std::variant<NodeValue<const int>,
+                                        NodeValue<const long int>,
+                                        NodeValue<const double>,
+                                        NodeValue<const TinyVector<1,double>>,
+                                        NodeValue<const TinyVector<2,double>>,
+                                        NodeValue<const TinyVector<3,double>>,
+
+                                        CellValue<const int>,
+                                        CellValue<const long int>,
+                                        CellValue<const double>,
+                                        CellValue<const TinyVector<1,double>>,
+                                        CellValue<const TinyVector<2,double>>,
+                                        CellValue<const TinyVector<3,double>>
+                                        >;
+
+ private:
+  std::map<std::string, ItemValueVariant> m_name_itemvariant_map;
+
+  template <typename DataType,
+            ItemType item_type>
+  PASTIS_FORCEINLINE
+  constexpr void _doInsert(const NamedItemValue<DataType, item_type>& named_itemvalue)
+  {
+    if (m_name_itemvariant_map.find(named_itemvalue.name()) == m_name_itemvariant_map.end()) {
+      m_name_itemvariant_map[named_itemvalue.name()] = named_itemvalue.itemValue();
+    }
+  }
+
+  template <typename DataType,
+            ItemType item_type,
+            typename... Args>
+  PASTIS_FORCEINLINE
+  constexpr void _unpackVariadicInput(const NamedItemValue<DataType, item_type>& named_itemvalue,
+                                      Args&&... args)
+  {
+    _doInsert(named_itemvalue);
+    if constexpr (sizeof...(args) > 0) {
+        this->_unpackVariadicInput(std::forward<Args>(args)...);
+      }
+  }
+
+ public:
+  auto begin() const
+  {
+    return m_name_itemvariant_map.begin();
+  }
+
+  auto end() const
+  {
+    return m_name_itemvariant_map.end();
+  }
+
+  template <typename ...DataType,
+            ItemType ...item_type>
+  OutputNamedItemValueSet(NamedItemValue<DataType, item_type>... named_itemvalue)
+  {
+    _unpackVariadicInput(named_itemvalue...);
+  }
+
+  OutputNamedItemValueSet(const OutputNamedItemValueSet&) = default;
+  OutputNamedItemValueSet() = default;
+  ~OutputNamedItemValueSet() = default;
+};
+
+
+#endif // OUTPUT_NAMED_ITEM_VALUE_SET_HPP
diff --git a/src/output/VTKWriter.hpp b/src/output/VTKWriter.hpp
index 276bad33d5624f018b9d512743f4f7695a6213e5..ffed42738c967dde7b799cd7607eece281ce1ed5 100644
--- a/src/output/VTKWriter.hpp
+++ b/src/output/VTKWriter.hpp
@@ -9,6 +9,7 @@
 #include <IConnectivity.hpp>
 
 #include <ItemValue.hpp>
+#include <OutputNamedItemValueSet.hpp>
 
 class VTKWriter
 {
@@ -18,9 +19,117 @@ class VTKWriter
   double m_last_time;
   const double m_time_period;
 
+  template <typename DataType> struct VTKType {};
+
+  template <typename DataType>
+  void _write_array(std::ofstream& os,
+                    const std::string& name,
+                    const Array<DataType>& item_value)
+  {
+    os << "<DataArray type=\"" << VTKType<DataType>::name
+       << "\" Name=\"" << name << "\">\n";
+    for (typename Array<DataType>::index_type i=0;
+         i<item_value.size(); ++i) {
+      // The following '+' enforces integer output for char types
+      os << +item_value[i] << ' ';
+    }
+    os << "\n</DataArray>\n";
+  }
+
+  template <size_t N,
+            typename DataType>
+  void _write_array(std::ofstream& os,
+                    const std::string& name,
+                    const Array<TinyVector<N, DataType>>& item_value)
+  {
+    os << "<DataArray type=\"" << VTKType<DataType>::name
+       << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N << "\">\n";
+    for (typename Array<DataType>::index_type i=0;
+         i<item_value.size(); ++i) {
+      for (size_t j=0; j<N; ++j) {
+        // The following '+' enforces integer output for char types
+        os << +item_value[i][j] << ' ';
+      }
+    }
+    os << "\n</DataArray>\n";
+  }
+
+  template <typename DataType>
+  void _write_node_value(std::ofstream& os,
+                         const std::string& name,
+                         const NodeValue<const DataType>& item_value)
+  {
+    os << "<DataArray type=\"" << VTKType<DataType>::name
+       << "\" Name=\"" << name << "\">\n";
+    for (NodeId i=0; i<item_value.size(); ++i) {
+      // The following '+' enforces integer output for char types
+      os << +item_value[i] << ' ';
+    }
+    os << "\n</DataArray>\n";
+  }
+
+  template <size_t N,
+            typename DataType>
+  void _write_node_value(std::ofstream& os,
+                         const std::string& name,
+                         const NodeValue<const TinyVector<N, DataType>>& item_value)
+  {
+    os << "<DataArray type=\"" << VTKType<DataType>::name
+       << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N <<  "\">\n";
+    for (NodeId i=0; i<item_value.size(); ++i) {
+      for (size_t j=0; j<N; ++j) {
+      // The following '+' enforces integer output for char types
+        os << +item_value[i][j] << ' ';
+      }
+    }
+    os << "\n</DataArray>\n";
+  }
+
+  template <typename DataType>
+  void _write_node_value(std::ofstream&,
+                         const std::string&,
+                         const CellValue<const DataType>&) {}
+
+  template <typename DataType>
+  void _write_cell_value(std::ofstream& os,
+                         const std::string& name,
+                         const CellValue<const DataType>& item_value)
+  {
+    os << "<DataArray type=\"" << VTKType<DataType>::name
+       << "\" Name=\"" << name << "\">\n";
+    for (CellId i=0; i<item_value.size(); ++i) {
+      // The following '+' enforces integer output for char types
+      os << +item_value[i] << ' ';
+    }
+    os << "\n</DataArray>\n";
+  }
+
+  template <size_t N,
+            typename DataType>
+  void _write_cell_value(std::ofstream& os,
+                         const std::string& name,
+                         const CellValue<const TinyVector<N, DataType>>& item_value)
+  {
+    os << "<DataArray type=\"" << VTKType<DataType>::name
+       << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N <<  "\">\n";
+    for (CellId i=0; i<item_value.size(); ++i) {
+      for (size_t j=0; j<N; ++j) {
+      // The following '+' enforces integer output for char types
+        os << +item_value[i][j] << ' ';
+      }
+    }
+    os << "\n</DataArray>\n";
+  }
+
+  template <typename DataType>
+  void _write_cell_value(std::ofstream&,
+                   const std::string&,
+                   const NodeValue<const DataType>&) {}
+
  public:
   template <typename MeshType>
   void write(const MeshType& mesh,
+             const OutputNamedItemValueSet& output_named_item_value_set,
              const double& time,
              const bool& forced_output = false)
   {
@@ -38,96 +147,98 @@ class VTKWriter
     fout << "<UnstructuredGrid>\n";
     fout << "<Piece NumberOfPoints=\""<< mesh.numberOfNodes()
          << "\" NumberOfCells=\"" << mesh.numberOfCells() << "\">\n";
-
+    fout << "<CellData>\n";
+    for(const auto& [name, item_value_variant] : output_named_item_value_set) {
+      std::visit([&, name=name](auto&& item_value) {
+                   return this->_write_cell_value(fout, name, item_value);
+                 }, item_value_variant);
+    }
+    fout << "</CellData>\n";
+    fout << "<PointData>\n";
+    for(const auto& [name, item_value_variant] : output_named_item_value_set) {
+      std::visit([&, name=name](auto&& item_value) {
+                   return this->_write_node_value(fout, name, item_value);
+                 }, item_value_variant);
+    }
+    fout << "</PointData>\n";
     fout << "<Points>\n";
-    fout << "<DataArray Name=\"Positions\" NumberOfComponents=\"3\" type=\"Float64\" format=\"ascii\">\n";
-    using Rd = TinyVector<MeshType::dimension>;
-    const NodeValue<const Rd>& xr = mesh.xr();
-    if constexpr(MeshType::dimension == 1) {
-      for (NodeId r=0; r<mesh.numberOfNodes(); ++r) {
-        for (unsigned short i=0; i<1; ++i) {
-          fout << xr[r][i] << ' ';
-        }
-        fout << "0 0 "; // VTK requires 3 components
-      }
-    } else if constexpr (MeshType::dimension == 2) {
+    {
+      using Rd = TinyVector<MeshType::dimension>;
+      const NodeValue<const Rd>& xr = mesh.xr();
+      Array<TinyVector<3>> positions(mesh.numberOfNodes());
       for (NodeId r=0; r<mesh.numberOfNodes(); ++r) {
-        for (unsigned short i=0; i<2; ++i) {
-          fout << xr[r][i] << ' ';
+        for (unsigned short i=0; i<MeshType::dimension; ++i) {
+          positions[r][i] = xr[r][i];
         }
-        fout << "0 "; // VTK requires 3 components
-      }
-    } else {
-      for (NodeId r=0; r<mesh.numberOfNodes(); ++r) {
-        for (unsigned short i=0; i<3; ++i) {
-          fout << xr[r][i] << ' ';
+        for (unsigned short i=MeshType::dimension; i<3; ++i) {
+          positions[r][i] = 0;
         }
       }
+      _write_array(fout, "Positions", positions);
     }
-    fout << '\n';
-    fout << "</DataArray>\n";
     fout << "</Points>\n";
 
     fout << "<Cells>\n";
 
-    fout << "<DataArray type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\">\n";
-
-    const auto& cell_to_node_matrix
-        = mesh.connectivity().cellToNodeMatrix();
+    {
+      const auto& cell_to_node_matrix
+          = mesh.connectivity().cellToNodeMatrix();
+      Array<int> connectivity(cell_to_node_matrix.numberOfEntries());
 
-    for (CellId j=0; j<mesh.numberOfCells(); ++j) {
-      const auto& cell_nodes = cell_to_node_matrix[j];
-      for (unsigned short r=0; r<cell_nodes.size(); ++r) {
-        fout << cell_nodes[r] << ' ';
+      for (size_t i=0; i<cell_to_node_matrix.numberOfEntries(); ++i) {
+        connectivity[i] = cell_to_node_matrix.entries()[i];
       }
+      _write_array(fout, "connectivity", connectivity);
     }
-    fout << '\n';
-    fout << "</DataArray>\n";
 
-    fout << "<DataArray type=\"UInt32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\">\n";
     {
+      const auto& cell_to_node_matrix
+          = mesh.connectivity().cellToNodeMatrix();
+      Array<unsigned int> offsets(mesh.numberOfCells());
       unsigned int offset=0;
       for (CellId j=0; j<mesh.numberOfCells(); ++j) {
         const auto& cell_nodes = cell_to_node_matrix[j];
         offset += cell_nodes.size();
-        fout << offset << ' ';
+        offsets[j]=offset;
       }
+      _write_array(fout, "offsets", offsets);
     }
-    fout << '\n';
-    fout << "</DataArray>\n";
-
-    fout << "<DataArray type=\"Int8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\">\n";
-    for (CellId j=0; j<mesh.numberOfCells(); ++j) {
-      const auto& cell_nodes = cell_to_node_matrix[j];
-      switch (cell_nodes.size()) {
-        case 2: {
-          fout << "3 ";
-          break;
-        }
-        case 3: {
-          fout << "5 ";
-          break;
-        }
-        case 4: {
-          if (mesh.meshDimension() == 3) {
-            fout << "10 ";
-          } else {
-            fout << "9 ";
+
+    {
+      const auto& cell_to_node_matrix
+          = mesh.connectivity().cellToNodeMatrix();
+      Array<int8_t> types(mesh.numberOfCells());
+      for (CellId j=0; j<mesh.numberOfCells(); ++j) {
+        const auto& cell_nodes = cell_to_node_matrix[j];
+        switch (cell_nodes.size()) {
+          case 2: {
+            types[j]=3;
+            break;
+          }
+          case 3: {
+            types[j]=5;
+            break;
+          }
+          case 4: {
+            if (mesh.meshDimension() == 3) {
+              types[j]=10;
+            } else {
+              types[j]=9;
+            }
+            break;
+          }
+          case 8: {
+            types[j]=12;
+            break;
+          }
+          default: {
+            types[j]=7;
+            break;
           }
-          break;
-        }
-        case 8: {
-          fout << "12 ";
-          break;
-        }
-        default: {
-          fout << "7 ";
-          break;
         }
       }
+      _write_array(fout, "types", types);
     }
-    fout << '\n';
-    fout << "</DataArray>\n";
 
     fout << "</Cells>\n";
     fout << "</Piece>\n";
@@ -136,6 +247,7 @@ class VTKWriter
 
     m_file_number++;
   }
+
   VTKWriter(const std::string& base_filename,
             const double time_period)
       : m_base_filename(base_filename),
@@ -147,4 +259,55 @@ class VTKWriter
   ~VTKWriter() = default;
 };
 
+template <>
+struct VTKWriter::VTKType<int8_t>
+{
+  inline const static std::string name{"Int8"};
+};
+
+template <> struct VTKWriter::VTKType<uint8_t>
+{
+  inline const static std::string name{"UInt8"};
+};
+
+template <> struct VTKWriter::VTKType<int16_t>
+{
+  inline const static std::string name{"Int16"};
+};
+
+template <> struct VTKWriter::VTKType<uint16_t>
+{
+  inline const static std::string name{"UInt16"};
+};
+
+template <> struct VTKWriter::VTKType<int32_t>
+{
+  inline const static std::string name{"Int32"};
+};
+
+template <> struct VTKWriter::VTKType<uint32_t>
+{
+  inline const static std::string name{"UInt32"};
+};
+
+template <> struct VTKWriter::VTKType<int64_t>
+{
+  inline const static std::string name{"Int64"};
+};
+
+template <> struct VTKWriter::VTKType<uint64_t>
+{
+  inline const static std::string name{"UInt64"};
+};
+
+template <> struct VTKWriter::VTKType<float>
+{
+  inline const static std::string name{"Float32"};
+};
+
+template <> struct VTKWriter::VTKType<double>
+{
+  inline const static std::string name{"Float64"};
+};
+
 #endif // VTK_WRITER_HPP