diff --git a/dumux/io/vtkoutputmodule.hh b/dumux/io/vtkoutputmodule.hh
index daf41b0b1933458f5d65ac27d752d8d95830fe03..f1f115f9d7e906541b89d57dfa36335a234c56fb 100644
--- a/dumux/io/vtkoutputmodule.hh
+++ b/dumux/io/vtkoutputmodule.hh
@@ -235,6 +235,24 @@ public:
         }
     }
 
+protected:
+    // some return functions for differing implementations to use
+    const Problem& problem() const { return problem_; }
+    const FVGridGeometry& fvGridGeometry() const { return gridGeom_; }
+    const GridVariables& gridVariables() const { return gridVariables_; }
+    const SolutionVector& sol() const { return sol_; }
+
+    const bool verbose() const { return verbose_; }
+    const std::string& name() const { return name_; }
+    const Dune::VTK::DataMode dataMode() const { return dm_; }
+
+    Dune::VTKWriter<GridView>& writer() { return *writer_; }
+    Dune::VTKSequenceWriter<GridView>& sequenceWriter() { return sequenceWriter_; }
+
+    const std::vector<VolVarScalarDataInfo>& volVarScalarDataInfo() const { return volVarScalarDataInfo_; }
+    const std::vector<VolVarVectorDataInfo>& volVarVectorDataInfo() const { return volVarVectorDataInfo_; }
+    const std::vector<Field>& fields() const { return fields_; }
+
 private:
 
     //! Assembles the fields and adds them to the writer (conforming output)
diff --git a/dumux/porousmediumflow/boxdfm/vtkoutputmodule.hh b/dumux/porousmediumflow/boxdfm/vtkoutputmodule.hh
new file mode 100644
index 0000000000000000000000000000000000000000..59b06c098804654602a9b2c772f7e5c1fe258d45
--- /dev/null
+++ b/dumux/porousmediumflow/boxdfm/vtkoutputmodule.hh
@@ -0,0 +1,416 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 2 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup InputOutput
+ * \brief A VTK output module to simplify writing dumux simulation data to VTK format
+ */
+#ifndef POROUSMEDIUMFLOW_BOXDFM_VTK_OUTPUT_MODULE_HH
+#define POROUSMEDIUMFLOW_BOXDFM_VTK_OUTPUT_MODULE_HH
+
+#include <set>
+
+#include <dune/grid/common/gridfactory.hh>
+#include <dune/geometry/referenceelements.hh>
+#include <dune/grid/common/mcmgmapper.hh>
+
+#include <dumux/io/vtkoutputmodule.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup InputOutput
+ * \brief A VTK output module to simplify writing dumux simulation data to VTK format.
+ *        This output module is specialized for writing out data obtained by the box-dfm
+ *        scheme. It writes out separate vtk files for the solution on the fracture. For
+ *        this, a grid type to be used the fracture-conforming lower-dimensional grid has
+ *        to be provided.
+ *
+ * \tparam TypeTag The TypeTag of the problem implementation
+ * \tparam FractureGrid The Type used for the lower-dimensional grid
+ * \tparam phaseIdxOffset Used for single-phase problems to retrieve the right phase name
+ *
+ * Handles the output of scalar and vector fields to VTK formatted file for multiple
+ * variables and timesteps. Certain predefined fields can be registered on
+ * initialization and/or be turned on/off using the designated properties. Additionally
+ * non-standardized scalar and vector fields can be added to the writer manually.
+ */
+template<class TypeTag, class FractureGrid, int phaseIdxOffset = 0>
+class BoxDfmVtkOutputModule : public VtkOutputModule<TypeTag, phaseIdxOffset>
+{
+    using ParentType = VtkOutputModule<TypeTag, phaseIdxOffset>;
+
+    using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
+    using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables);
+    using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector);
+    using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry);
+    using VelocityOutput = typename GET_PROP_TYPE(TypeTag, VelocityOutput);
+    using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits);
+    static constexpr int numPhases = ModelTraits::numPhases();
+
+    using VV = typename GridVariables::VolumeVariables;
+    using FluidSystem = typename VV::FluidSystem;
+    using Scalar = typename GridVariables::Scalar;
+
+    using GridView = typename FVGridGeometry::GridView;
+    using FractureGridView = typename FractureGrid::LeafGridView;
+    using FractureVertexMapper = Dune::MultipleCodimMultipleGeomTypeMapper<FractureGridView>;
+
+    enum
+    {
+        dim = GridView::dimension,
+        dimWorld = GridView::dimensionworld
+    };
+
+    using IndexType = typename GridView::IndexSet::IndexType;
+    using Element = typename GridView::template Codim<0>::Entity;
+    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
+    using ReferenceElements = typename Dune::ReferenceElements<typename GridView::ctype, dim>;
+
+    using Field = Vtk::template Field<GridView>;
+    using FractureField = Vtk::template Field<FractureGridView>;
+
+    static_assert(dim > 1, "Box-Dfm output only works for dim > 1");
+    static_assert(FractureGrid::dimension == int(dim-1), "Fracture grid must be of codimension one!");
+    static_assert(FractureGrid::dimensionworld == int(dimWorld), "Fracture grid has to has the same coordinate dimension!");
+    static_assert(FVGridGeometry::discMethod == DiscretizationMethod::box, "Box-Dfm output module can only be used with the box scheme!");
+public:
+
+    BoxDfmVtkOutputModule(const Problem& problem,
+                          const FVGridGeometry& fvGridGeometry,
+                          const GridVariables& gridVariables,
+                          const SolutionVector& sol,
+                          const std::string& name,
+                          const std::string& paramGroup = "",
+                          Dune::VTK::DataMode dm = Dune::VTK::conforming,
+                          bool verbose = true)
+    : ParentType(problem, fvGridGeometry, gridVariables, sol, name, paramGroup, dm, verbose)
+    {
+        const auto& gridView = fvGridGeometry.gridView();
+        Dune::GridFactory<FractureGrid> gridFactory;
+
+        // insert fracture vertices
+        std::size_t fracVertexCount = 0;
+        vertexToFractureVertexIdx_.resize(gridView.size(dim));
+        for (const auto& v : vertices(gridView))
+        {
+            const auto vIdxGlobal = fvGridGeometry.vertexMapper().index(v);
+            if (fvGridGeometry.dofOnFracture(vIdxGlobal))
+            {
+                gridFactory.insertVertex(v.geometry().center());
+                vertexToFractureVertexIdx_[vIdxGlobal] = fracVertexCount++;
+            }
+        }
+
+        // insert fracture elements
+        std::set< std::pair<IndexType, unsigned int> > handledFacets;
+        auto vertIsOnFracture = [&fvGridGeometry] (auto idx) { return fvGridGeometry.dofOnFracture(idx); };
+        for (const auto& element : elements(gridView))
+        {
+            const auto referenceElement = ReferenceElements::general(element.geometry().type());
+
+            for (const auto& is : intersections(gridView, element))
+            {
+                // obtain all vertex indices on this intersection
+                const auto& isGeometry = is.geometry();
+                const auto numCorners = isGeometry.corners();
+
+                std::vector<IndexType> isVertexIndices(numCorners);
+                for (unsigned int i = 0; i < numCorners; ++i)
+                {
+                    const auto vIdxLocal = referenceElement.subEntity(is.indexInInside(), 1, i, dim);
+                    isVertexIndices[i] = fvGridGeometry.vertexMapper().subIndex(element, vIdxLocal, dim);
+                }
+
+                // determine if this is a fracture facet & if it has to be inserted
+                bool insertFacet = false;
+                if ( std::all_of(isVertexIndices.begin(), isVertexIndices.end(), vertIsOnFracture) )
+                {
+                    insertFacet = true;
+                    if (!is.boundary())
+                    {
+                        // only proceed if facet has not been handled yet
+                        const auto pair = std::make_pair<IndexType, unsigned int>(fvGridGeometry.elementMapper().index(is.outside()),
+                                                                                  is.indexInOutside());
+                        if (handledFacets.count( pair ) != 0)
+                            insertFacet = false;
+                    }
+                }
+
+                if (insertFacet)
+                {
+                    // transform intersection vertex indices to frac grid indices
+                    std::for_each( isVertexIndices.begin(),
+                                   isVertexIndices.end(),
+                                   [&] (auto& idx) { idx = this->vertexToFractureVertexIdx_[idx]; } );
+
+                    // insert the element
+                    gridFactory.insertElement(isGeometry.type(), isVertexIndices);
+
+                    // insert to set of handled facets
+                    handledFacets.insert( std::make_pair<IndexType, unsigned int>(fvGridGeometry.elementMapper().index(element),
+                                                                                  is.indexInInside()) );
+                }
+            }
+        }
+
+        auto grid = gridFactory.createGrid();
+        fractureGridView_ = std::make_unique<FractureGridView>(grid->leafGridView());
+
+        // update fracture vertex mapper
+        fractureVertexMapper_ = std::make_unique<FractureVertexMapper>(*fractureGridView_, Dune::mcmgVertexLayout());
+
+        // obtain map fracture vertex insertion index -> fracture grid vertex index
+        std::vector<IndexType> insToVertexIdx(fractureGridView_->size(FractureGridView::dimension));
+        for (const auto& v : vertices(*fractureGridView_))
+            insToVertexIdx[ gridFactory.insertionIndex(v) ] = fractureVertexMapper_->index(v);
+
+        // update the index map
+        for (IndexType dofIdx = 0; dofIdx < gridView.size(GridView::dimension); ++dofIdx)
+            if (fvGridGeometry.dofOnFracture(dofIdx))
+                vertexToFractureVertexIdx_[dofIdx] = insToVertexIdx[ vertexToFractureVertexIdx_[dofIdx] ];
+
+        // instantiate writers for the fracture
+        fractureWriter_ = std::make_shared< Dune::VTKWriter<FractureGridView> >(*fractureGridView_, dm);
+        fractureSequenceWriter_ = std::make_unique< Dune::VTKSequenceWriter<FractureGridView> >(fractureWriter_, name + "_fracture");
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    //! Writing data
+    //////////////////////////////////////////////////////////////////////////////////////////////
+
+    //! Write the data for this timestep to file in four steps
+    //! (1) We assemble all registered variable fields
+    //! (2) We register them with the vtk writer
+    //! (3) The writer writes the output for us
+    //! (4) Clear the writer for the next time step
+    void write(double time, Dune::VTK::OutputType type = Dune::VTK::ascii)
+    {
+        Dune::Timer timer;
+
+        // write to file depending on data mode
+        const auto dm = this->dataMode();
+        if (dm == Dune::VTK::conforming)
+            writeConforming_(time, type);
+        else if (dm == Dune::VTK::nonconforming)
+            writeNonConforming_(time, type);
+        else
+            DUNE_THROW(Dune::NotImplemented, "Output for provided vtk data mode");
+
+        //! output
+        timer.stop();
+        if (this->verbose())
+            std::cout << "Writing output for problem \"" << this->name() << "\". Took " << timer.elapsed() << " seconds." << std::endl;
+    }
+
+private:
+    //! Assembles the fields and adds them to the writer (conforming output)
+    void writeConforming_(double time, Dune::VTK::OutputType type)
+    {
+        //////////////////////////////////////////////////////////////
+        //! (1) Assemble all variable fields and add to writer
+        //////////////////////////////////////////////////////////////
+
+        // instatiate the velocity output
+        VelocityOutput velocityOutput(this->problem(), this->fvGridGeometry(), this->gridVariables(), this->sol());
+        std::array<std::vector<GlobalPosition>, numPhases> velocity;
+
+        // process rank
+        static bool addProcessRank = getParamFromGroup<bool>(this->paramGroup(), "Vtk.AddProcessRank");
+        std::vector<double> rank;
+
+        // volume variable data
+        std::vector<std::vector<Scalar>> volVarScalarData;
+        std::vector<std::vector<Scalar>> volVarScalarDataFracture;
+        std::vector<std::vector<GlobalPosition>> volVarVectorData;
+        std::vector<std::vector<GlobalPosition>> volVarVectorDataFracture;
+
+        // some references for convenience
+        const auto& gridView = this->fvGridGeometry().gridView();
+        const auto& volVarScalarDataInfo = this->volVarScalarDataInfo();
+        const auto& volVarVectorDataInfo = this->volVarVectorDataInfo();
+
+        //! Abort if no data was registered
+        if (!volVarScalarDataInfo.empty()
+            || !volVarVectorDataInfo.empty()
+            || !this->fields().empty()
+            || velocityOutput.enableOutput()
+            || addProcessRank)
+        {
+            const auto numCells = gridView.size(0);
+            const auto numDofs = gridView.size(dim);
+            const auto numFractureVert = fractureGridView_->size(FractureGridView::dimension);
+
+            // get fields for all volume variables
+            if (!this->volVarScalarDataInfo().empty())
+            {
+                volVarScalarData.resize(volVarScalarDataInfo.size(), std::vector<Scalar>(numDofs));
+                volVarScalarDataFracture.resize(volVarScalarDataInfo.size(), std::vector<Scalar>(numFractureVert));
+            }
+            if (!this->volVarVectorDataInfo().empty())
+            {
+                volVarVectorData.resize(volVarVectorDataInfo.size(), std::vector<GlobalPosition>(numDofs));
+                volVarVectorDataFracture.resize(volVarVectorDataInfo.size(), std::vector<GlobalPosition>(numFractureVert));
+            }
+
+            if (velocityOutput.enableOutput())
+                for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                    velocity[phaseIdx].resize(numDofs);
+
+            // maybe allocate space for the process rank
+            if (addProcessRank) rank.resize(numCells);
+
+            for (const auto& element : elements(gridView, Dune::Partitions::interior))
+            {
+                const auto eIdxGlobal = this->fvGridGeometry().elementMapper().index(element);
+
+                auto fvGeometry = localView(this->fvGridGeometry());
+                auto elemVolVars = localView(this->gridVariables().curGridVolVars());
+
+                // If velocity output is enabled we need to bind to the whole stencil
+                // otherwise element-local data is sufficient
+                if (velocityOutput.enableOutput())
+                {
+                    fvGeometry.bind(element);
+                    elemVolVars.bind(element, fvGeometry, this->sol());
+                }
+                else
+                {
+                    fvGeometry.bindElement(element);
+                    elemVolVars.bindElement(element, fvGeometry, this->sol());
+                }
+
+                if (!volVarScalarDataInfo.empty() || !volVarVectorDataInfo.empty())
+                {
+                    for (auto&& scv : scvs(fvGeometry))
+                    {
+                        const auto dofIdxGlobal = scv.dofIndex();
+                        const auto& volVars = elemVolVars[scv];
+
+                        // get the scalar-valued data
+                        if (!scv.isOnFracture())
+                        {
+                            for (std::size_t i = 0; i < volVarScalarDataInfo.size(); ++i)
+                                volVarScalarData[i][dofIdxGlobal] = volVarScalarDataInfo[i].get(volVars);
+                        }
+                        else
+                        {
+                            for (std::size_t i = 0; i < volVarScalarDataInfo.size(); ++i)
+                                volVarScalarDataFracture[i][vertexToFractureVertexIdx_[dofIdxGlobal]] = volVarScalarDataInfo[i].get(volVars);
+                        }
+
+                        // get the vector-valued data
+                        if (!scv.isOnFracture())
+                        {
+                            for (std::size_t i = 0; i < volVarVectorDataInfo.size(); ++i)
+                                volVarVectorData[i][dofIdxGlobal] = volVarVectorDataInfo[i].get(volVars);
+                        }
+                        else
+                        {
+                            for (std::size_t i = 0; i < volVarVectorDataInfo.size(); ++i)
+                                volVarVectorDataFracture[i][vertexToFractureVertexIdx_[dofIdxGlobal]] = volVarVectorDataInfo[i].get(volVars);
+                        }
+                    }
+                }
+
+                // velocity output
+                if (velocityOutput.enableOutput())
+                    for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                        velocityOutput.calculateVelocity(velocity[phaseIdx], elemVolVars, fvGeometry, element, phaseIdx);
+
+                //! the rank
+                if (addProcessRank)
+                    rank[eIdxGlobal] = static_cast<double>(gridView.comm().rank());
+            }
+
+            //////////////////////////////////////////////////////////////
+            //! (2) Register data fields with the vtk writer
+            //////////////////////////////////////////////////////////////
+
+            // volume variables if any
+            for (std::size_t i = 0; i < volVarScalarDataInfo.size(); ++i)
+            {
+                this->sequenceWriter().addVertexData(volVarScalarData[i], volVarScalarDataInfo[i].name);
+                fractureSequenceWriter_->addVertexData(volVarScalarDataFracture[i], volVarScalarDataInfo[i].name);
+            }
+
+            for (std::size_t i = 0; i < volVarVectorDataInfo.size(); ++i)
+            {
+                this->sequenceWriter().addVertexData( Field(gridView, this->fvGridGeometry().vertexMapper(), volVarVectorData[i],
+                                                            volVarVectorDataInfo[i].name, /*numComp*/dimWorld, /*codim*/dim).get() );
+                fractureSequenceWriter_->addVertexData( FractureField(*fractureGridView_, *fractureVertexMapper_, volVarVectorDataFracture[i],
+                                                                      volVarVectorDataInfo[i].name, /*numComp*/dimWorld, /*codim*/dim-1).get() );
+            }
+
+            // the velocity field
+            if (velocityOutput.enableOutput())
+            {
+                for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx)
+                    this->sequenceWriter().addVertexData( Field(gridView, this->fvGridGeometry().vertexMapper(), velocity[phaseIdx],
+                                                                "velocity_" + std::string(FluidSystem::phaseName(phaseIdx+phaseIdxOffset)) + " (m/s)",
+                                                                /*numComp*/dimWorld, /*codim*/dim).get() );
+            }
+
+            // the process rank
+            if (addProcessRank)
+                this->sequenceWriter().addCellData( Field(gridView, this->fvGridGeometry().elementMapper(), rank,
+                                                          "process rank", /*numComp*/1, /*codim*/0).get() );
+
+            // also register additional (non-standardized) user fields if any (only on matrix grid)
+            for (auto&& field : this->fields())
+            {
+                if (field.codim() == 0)
+                    this->sequenceWriter().addCellData(field.get());
+                else if (field.codim() == dim)
+                    this->sequenceWriter().addVertexData(field.get());
+                else
+                    DUNE_THROW(Dune::RangeError, "Cannot add wrongly sized vtk scalar field!");
+            }
+        }
+
+        //////////////////////////////////////////////////////////////
+        //! (2) The writer writes the output for us
+        //////////////////////////////////////////////////////////////
+        this->sequenceWriter().write(time, type);
+        fractureSequenceWriter_->write(time, type);
+
+        //////////////////////////////////////////////////////////////
+        //! (3) Clear the writer
+        //////////////////////////////////////////////////////////////
+        this->writer().clear();
+        fractureWriter_->clear();
+    }
+
+    //! Assembles the fields and adds them to the writer (conforming output)
+    void writeNonConforming_(double time, Dune::VTK::OutputType type)
+    {
+        DUNE_THROW(Dune::NotImplemented, "Non-conforming output for Box-Dfm model");
+    }
+
+    std::unique_ptr<FractureVertexMapper> fractureVertexMapper_;
+    std::unique_ptr<FractureGridView> fractureGridView_;
+    std::shared_ptr<Dune::VTKWriter<FractureGridView>> fractureWriter_;
+    std::unique_ptr< Dune::VTKSequenceWriter<FractureGridView> > fractureSequenceWriter_;
+
+    std::vector<IndexType> vertexToFractureVertexIdx_;
+};
+
+} // end namespace Dumux
+
+#endif