diff --git a/heart-core.cabal b/heart-core.cabal
--- a/heart-core.cabal
+++ b/heart-core.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 507050e1190f946f7bd7befa6b9d911d927f5f1737a960c724047c1a6956bbe1
+-- hash: effa1e2d3414d60241a02d0d77c5989b8a5a55d81b56c5ac194f1b2da2e79572
 
 name:           heart-core
-version:        0.1.1
+version:        0.2.0
 synopsis:       An opinionated library prelude in the UnliftIO style
 description:    Please see the README on GitHub at <https://github.com/ejconlon/heart-core#readme>
 category:       Prelude
@@ -49,6 +49,7 @@
     , aeson-casing >=0.2
     , base >=4.12 && <5
     , containers >=0.6
+    , deepseq >=1.4
     , exceptions >=0.10
     , filepath >=1.4
     , hashable >=1.2
diff --git a/src/Heart/Core/Aeson.hs b/src/Heart/Core/Aeson.hs
--- a/src/Heart/Core/Aeson.hs
+++ b/src/Heart/Core/Aeson.hs
@@ -9,7 +9,7 @@
   , HasTagPrefix (..)
   ) where
 
-import Control.Newtype.Generics (Newtype, O, pack, unpack)
+import Control.Newtype.Generics (O, pack, unpack)
 import Data.Aeson
 import Data.Aeson.Casing (aesonPrefix, snakeCase)
 import qualified Data.Text as Text
diff --git a/src/Heart/Core/Prelude.hs b/src/Heart/Core/Prelude.hs
--- a/src/Heart/Core/Prelude.hs
+++ b/src/Heart/Core/Prelude.hs
@@ -14,6 +14,7 @@
   , Identity (..)
   , Int64
   , Iso'
+  , IsList
   , IsString
   , Lens'
   , ListT (..)
@@ -23,10 +24,12 @@
   , MonadIO (..)
   , MonadMask (..)
   , MonadReader (..)
+  , MonadState (..)
   , MonadThrow (..)
   , MonadTrans (..)
   , MonadUnliftIO (..)
   , Newtype
+  , NFData
   , Prism'
   , Proxy (..)
   , Rep
@@ -53,19 +56,23 @@
   , iso
   , makeLenses
   , makePrisms
+  , modifying
   , over
   , review
   , set
   , simple
   , toList
   , unless
+  , use
   , view
   , when
   ) where
 
 import Control.Applicative (Alternative (..))
+import Control.DeepSeq (NFData)
 import Control.Exception (Exception (..), SomeException)
-import Control.Lens (Getter, Iso', Lens', Prism', Setter', coerced, iso, over, review, set, simple, view)
+import Control.Lens (Getter, Iso', Lens', Prism', Setter', coerced, iso, modifying, over, review, set, simple, use,
+                     view)
 import Control.Lens.TH (makeLenses, makePrisms)
 import Control.Monad (ap, foldM, unless, when)
 import Control.Monad.Catch (MonadCatch (..), MonadMask (..), MonadThrow (..), catchJust)
@@ -74,6 +81,7 @@
 import Control.Monad.IO.Class (MonadIO (..))
 import Control.Monad.IO.Unlift (MonadUnliftIO (..), UnliftIO (..))
 import Control.Monad.Reader (MonadReader (..))
+import Control.Monad.State (MonadState (..))
 import Control.Monad.Trans (MonadTrans (..))
 import Control.Newtype.Generics (Newtype)
 import Data.Aeson (FromJSON (..), FromJSONKey, ToJSON (..), ToJSONKey)
@@ -93,6 +101,7 @@
 import Data.Traversable (for)
 import Data.Typeable (Typeable, cast)
 import Data.Void (Void)
+import GHC.Exts (IsList)
 import GHC.Generics (Generic, Rep)
 import GHC.Stack (HasCallStack)
 import ListT (ListT (..))
diff --git a/src/Heart/Core/RIO.hs b/src/Heart/Core/RIO.hs
--- a/src/Heart/Core/RIO.hs
+++ b/src/Heart/Core/RIO.hs
@@ -1,5 +1,10 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE UndecidableInstances #-}
+
 module Heart.Core.RIO
-  ( RIO (..)
+  ( HasStateRef (..)
+  , RIO (..)
   , withRIO
   , runRIO
   ) where
@@ -7,6 +12,7 @@
 import Control.Monad.IO.Unlift (withUnliftIO)
 import Control.Monad.Reader (ReaderT (..))
 import Heart.Core.Prelude
+import UnliftIO.IORef (IORef, readIORef, writeIORef)
 
 newtype RIO env a = RIO { unRIO :: ReaderT env IO a }
   deriving (Functor, Applicative, Monad, MonadReader env, MonadIO, MonadThrow, MonadFail, MonadCatch, MonadMask)
@@ -20,5 +26,21 @@
   let env' = f env
   runRIO env' m
 
+liftRIO :: (MonadIO m, MonadReader env m) => RIO env a -> m a
+liftRIO rio = do
+  env <- ask
+  runRIO env rio
+
 runRIO :: MonadIO m => env -> RIO env a -> m a
 runRIO r m = liftIO (runReaderT (unRIO m) r)
+
+class HasStateRef st env | env -> st where
+  stateRefL :: Lens' env (IORef st)
+
+instance HasStateRef st env => MonadState st (RIO env) where
+  get = do
+    ref <- view stateRefL
+    readIORef ref
+  put st = do
+    ref <- view stateRefL
+    writeIORef ref st
