diff --git a/dune/grid/common/CMakeLists.txt b/dune/grid/common/CMakeLists.txt
index df2de386e245604404f9473288bdf7847141bb76..35edcb8f0e8b5b5a24f20303cbcf29e89c0bfe7a 100644
--- a/dune/grid/common/CMakeLists.txt
+++ b/dune/grid/common/CMakeLists.txt
@@ -27,7 +27,8 @@ set(HEADERS
   partitionset.hh
   rangegenerators.hh
   sizecache.hh
-  scsgmapper.hh)
+  scsgmapper.hh
+  userdata.hh)
 
 add_subdirectory(test)
 
diff --git a/dune/grid/common/grid.hh b/dune/grid/common/grid.hh
index 5f4e66b8109b3880c6339b2712125a955c362588..9fcb0c80c028098f100aeec3cc4775d88c0359fa 100644
--- a/dune/grid/common/grid.hh
+++ b/dune/grid/common/grid.hh
@@ -10,8 +10,10 @@
  */
 // system includes
 #include <iostream>
+#include <typeinfo>
 #include <string>
 #include <vector>
+#include <map>
 
 // dune-common includes
 #include <dune/common/fvector.hh>
@@ -29,6 +31,7 @@
 #include <dune/grid/common/gridview.hh>
 #include <dune/grid/common/defaultgridview.hh>
 #include <dune/grid/common/entityseed.hh>
+#include <dune/grid/common/userdata.hh>
 
 // include this file after all other, because other files might undef the
 // macros that are defined in that file
@@ -971,8 +974,42 @@ namespace Dune {
       return false;
     }
 
+    /*! \brief return shared_ptr to user data object of given type
+     *
+     * \return shared_ptr< UserData > which either contains a pointer to a
+     * previously registered object or is empty.
+     */
+    template <class UserData>
+    std::shared_ptr< UserData > getUserData() const
+    {
+      const std::string name( typeid( UserData ).name() );
+      auto it = userData_.find( name );
+      if( it != userData_.end() )
+        return it->second->template get< UserData >();
+      else
+        return std::shared_ptr< UserData >();
+    }
+
+    /*! \brief register shared_ptr to user data object provided as shared
+     *
+     *  \note: Only one object of each type can be registered.
+     */
+    template <class UserData>
+    void registerUserData( std::shared_ptr< UserData > userData ) const
+    {
+      const std::string name( typeid( UserData ).name() );
+      auto it = userData_.find( name );
+      if( it != userData_.end() )
+        DUNE_THROW(InvalidStateException,"addUserData: can only add a type once!");
+
+      typedef GridUserDataObject< UserData > ObjectType;
+      std::shared_ptr< GridUserDataIF > ptr( new ObjectType( userData ) );
+      userData_.insert( std::make_pair( name, ptr ) );
+    }
+
   protected:
     using Grid< dim, dimworld, ct, GridFamily >::asImp;
+    mutable std::map< const std::string, std::shared_ptr< GridUserDataIF > > userData_;
   };
 
   /** @} */
diff --git a/dune/grid/common/userdata.hh b/dune/grid/common/userdata.hh
new file mode 100644
index 0000000000000000000000000000000000000000..059a32ceea9b63d3f5b56ed8ceba5bff7f4781d2
--- /dev/null
+++ b/dune/grid/common/userdata.hh
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file LICENSE.md in module root
+// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+#ifndef DUNE_GRID_COMMON_USERDATA_HH
+#define DUNE_GRID_COMMON_USERDATA_HH
+
+#include <typeinfo>
+#include <memory>
+
+namespace Dune
+{
+  template <class Implementation>
+  class GridUserDataObject;
+
+  /** \brief interface class for user data storage
+   */
+  class GridUserDataIF
+  {
+  protected:
+    GridUserDataIF () {}
+  public:
+    virtual ~GridUserDataIF() {}
+    virtual std::string name() const { return std::string("interface"); }
+
+    template <class Implementation>
+    bool compare() const
+    {
+      return typeid(Implementation).name() == this->name();
+    }
+
+    template <class Implementation>
+    std::shared_ptr< Implementation >& get()
+    {
+      typedef GridUserDataObject< Implementation > GridUserDataObjectType;
+      //assert( dynamic_cast< GridUserDataObjectType& > (*this) );
+      return dynamic_cast< GridUserDataObjectType& > (*this).object();
+    }
+  };
+
+  template <class Implementation>
+  class GridUserDataObject : public GridUserDataIF
+  {
+    typedef std::shared_ptr< Implementation > ObjectPointerType;
+    ObjectPointerType obj_;
+  public:
+    GridUserDataObject( const ObjectPointerType& obj )
+      : obj_( obj )
+    {}
+
+    GridUserDataObject( ObjectPointerType&& obj )
+      : obj_( std::forward(obj) )
+    {}
+
+    std::string name() const override { return typeid(Implementation).name(); }
+
+    std::shared_ptr< Implementation >& object() { return obj_; }
+  };
+
+} // end namespace Dune
+
+#endif