diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,2 @@
+# strict-mutable-base-1.0.0.0 (2024-09-02)
+* Initial release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2024, Andrzej Rybczak
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Andrzej Rybczak nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# strict-mutable-base
+
+[![Hackage version](https://img.shields.io/hackage/v/strict-mutable-base.svg?label=Hackage)](https://hackage.haskell.org/package/strict-mutable-base)
+[![Build Status](https://github.com/arybczak/strict-mutable/actions/workflows/haskell-ci.yml/badge.svg?branch=master)](https://github.com/arybczak/strict-mutable/actions?query=branch%3Amaster)
+[![Dependencies](https://img.shields.io/hackage-deps/v/strict-mutable-base.svg)](https://packdeps.haskellers.com/feed?needle=andrzej@rybczak.net)
+[![Stackage LTS](https://www.stackage.org/package/strict-mutable-base/badge/lts)](https://www.stackage.org/lts/package/strict-mutable-base)
+[![Stackage Nightly](https://www.stackage.org/package/strict-mutable-base/badge/nightly)](https://www.stackage.org/nightly/package/strict-mutable-base)
+
+Strict (WHNF) variants of
+[Chan](https://hackage.haskell.org/package/base/docs/Control-Concurrent-Chan.html),
+[IORef](https://hackage.haskell.org/package/base/docs/Data-IORef.html) and
+[MVar](https://hackage.haskell.org/package/base/docs/Control-Concurrent-MVar.html)
+for proactive prevention of space leaks.
diff --git a/src/Control/Concurrent/Chan/Strict.hs b/src/Control/Concurrent/Chan/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Concurrent/Chan/Strict.hs
@@ -0,0 +1,47 @@
+-- | For full documentation please refer to "Control.Concurrent.Chan".
+module Control.Concurrent.Chan.Strict
+  ( Chan'
+
+    -- * Operations
+  , newChan'
+  , writeChan'
+  , readChan'
+  , dupChan'
+  , getChanContents'
+  , writeList2Chan'
+  ) where
+
+import Control.Exception (evaluate)
+import qualified Control.Concurrent.Chan as Base
+
+-- | A strict (WHNF) variant of 'Base.Chan'.
+newtype Chan' a = Chan' (Base.Chan a)
+  deriving Eq
+
+-- | 'Base.newChan' for 'Chan''.
+newChan' :: IO (Chan' a)
+newChan' = Chan' <$> Base.newChan
+
+-- | 'Base.writeChan' for 'Chan''.
+--
+-- Evaluates the value to WHNF.
+writeChan' :: Chan' a -> a -> IO ()
+writeChan' (Chan' chan) a = Base.writeChan chan =<< evaluate a
+
+-- | 'Base.readChan' for 'Chan''.
+readChan' :: Chan' a -> IO a
+readChan' (Chan' chan) = Base.readChan chan
+
+-- | 'Base.dupChan' for 'Chan''.
+dupChan' :: Chan' a -> IO (Chan' a)
+dupChan' (Chan' chan) = Chan' <$> Base.dupChan chan
+
+-- | 'Base.getChanContents' for 'Chan''.
+getChanContents' :: Chan' a -> IO [a]
+getChanContents' (Chan' chan) = Base.getChanContents chan
+
+-- | 'Base.writeList2Chan' for 'Chan''.
+--
+-- Evaluates the values to WHNF.
+writeList2Chan' :: Chan' a -> [a] -> IO ()
+writeList2Chan' = mapM_ . writeChan'
diff --git a/src/Control/Concurrent/MVar/Strict.hs b/src/Control/Concurrent/MVar/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Concurrent/MVar/Strict.hs
@@ -0,0 +1,135 @@
+-- | For full documentation please refer to "Control.Concurrent.MVar".
+module Control.Concurrent.MVar.Strict
+  ( MVar'
+
+    -- * Operations
+  , newEmptyMVar'
+  , newMVar'
+  , takeMVar'
+  , putMVar'
+  , readMVar'
+  , swapMVar'
+  , tryTakeMVar'
+  , tryPutMVar'
+  , tryReadMVar'
+  , isEmptyMVar'
+  , withMVar'
+  , withMVar'Masked
+  , modifyMVar'_
+  , modifyMVar'
+  , modifyMVar'Masked_
+  , modifyMVar'Masked
+  , mkWeakMVar'
+  ) where
+
+import Control.DeepSeq
+import Control.Exception (evaluate)
+import GHC.Exts (mkWeak#)
+import GHC.IO (IO(..))
+import GHC.MVar (MVar(..))
+import GHC.Weak (Weak(..))
+import qualified Control.Concurrent.MVar as Base
+
+-- | Strict (WHNF) version of 'MVar'.
+newtype MVar' a = MVar' (MVar a)
+  deriving (Eq, NFData, NFData1)
+
+-- | 'Base.newEmptyMVar' for an 'MVar''.
+newEmptyMVar' :: IO (MVar' a)
+newEmptyMVar' = MVar' <$> Base.newEmptyMVar
+
+-- | 'Base.newMVar' for an 'MVar''.
+--
+-- Evaluates the initial value to WHNF.
+newMVar' :: a -> IO (MVar' a)
+newMVar' a = fmap MVar' . Base.newMVar =<< evaluate a
+
+-- | 'Base.takeMVar' for an 'MVar''.
+takeMVar' :: MVar' a -> IO a
+takeMVar' (MVar' var) = Base.takeMVar var
+
+-- | 'Base.putMVar' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+putMVar' :: MVar' a -> a -> IO ()
+putMVar' (MVar' var) a = Base.putMVar var =<< evaluate a
+
+-- | 'Base.readMVar' for an 'MVar''.
+readMVar' :: MVar' a -> IO a
+readMVar' (MVar' var) = Base.readMVar var
+
+-- | 'Base.swapMVar' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+swapMVar' :: MVar' a -> a -> IO a
+swapMVar' (MVar' var) a = Base.swapMVar var =<< evaluate a
+
+-- | 'Base.tryTakeMVar' for an 'MVar''.
+tryTakeMVar' :: MVar' a -> IO (Maybe a)
+tryTakeMVar' (MVar' var) = Base.tryTakeMVar var
+
+-- | 'Base.tryPutMVar' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+tryPutMVar' :: MVar' a -> a -> IO Bool
+tryPutMVar' (MVar' var) a = Base.tryPutMVar var =<< evaluate a
+
+-- | 'Base.tryReadMVar' for an 'MVar''.
+tryReadMVar' :: MVar' a -> IO (Maybe a)
+tryReadMVar' (MVar' var) = Base.tryReadMVar var
+
+-- | 'Base.isEmptyMVar' for an 'MVar''.
+isEmptyMVar' :: MVar' a -> IO Bool
+isEmptyMVar' (MVar' var) = Base.isEmptyMVar var
+
+-- | 'Base.withMVar' for an 'MVar''.
+withMVar' :: MVar' a -> (a -> IO b) -> IO b
+withMVar' (MVar' var) action = Base.withMVar var action
+{-# INLINE withMVar' #-}
+
+-- | 'Base.withMVarMasked' for an 'MVar''.
+withMVar'Masked :: MVar' a -> (a -> IO b) -> IO b
+withMVar'Masked (MVar' var) action = Base.withMVarMasked var action
+{-# INLINE withMVar'Masked #-}
+
+-- | 'Base.modifyMVar_' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+modifyMVar'_ :: MVar' a -> (a -> IO a) -> IO ()
+modifyMVar'_ (MVar' var) action = Base.modifyMVar_ var $ \a0 -> do
+  a <- action a0
+  evaluate a
+{-# INLINE modifyMVar'_ #-}
+
+-- | 'Base.modifyMVar' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+modifyMVar' :: MVar' a -> (a -> IO (a, b)) -> IO b
+modifyMVar' (MVar' var) action = Base.modifyMVar var $ \a0 -> do
+  (a, b) <- action a0
+  (, b) <$> evaluate a
+{-# INLINE modifyMVar' #-}
+
+-- | 'Base.modifyMVarMasked_' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+modifyMVar'Masked_ :: MVar' a -> (a -> IO a) -> IO ()
+modifyMVar'Masked_ (MVar' var) action = Base.modifyMVarMasked_ var $ \a0 -> do
+  a <- action a0
+  evaluate a
+{-# INLINE modifyMVar'Masked_ #-}
+
+-- | 'Base.modifyMVarMasked' for an 'MVar''.
+--
+-- Evaluates the new value to WHNF.
+modifyMVar'Masked :: MVar' a -> (a -> IO (a, b)) -> IO b
+modifyMVar'Masked (MVar' var) action = Base.modifyMVarMasked var $ \a0 -> do
+  (a, b) <- action a0
+  (, b) <$> evaluate a
+{-# INLINE modifyMVar'Masked #-}
+
+-- | 'Base.mkWeakMVar' for an 'MVar''.
+mkWeakMVar' :: MVar' a -> IO () -> IO (Weak (MVar' a))
+mkWeakMVar' var@(MVar' (MVar var#)) (IO finalizer) = IO $ \s0 ->
+  case mkWeak# var# var finalizer s0 of
+    (# s1, w #) -> (# s1, Weak w #)
diff --git a/src/Data/IORef/Strict.hs b/src/Data/IORef/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/IORef/Strict.hs
@@ -0,0 +1,64 @@
+-- | For full documentation please refer to "Data.IORef".
+module Data.IORef.Strict
+  ( IORef'
+
+    -- * Operations
+  , newIORef'
+  , readIORef'
+  , writeIORef'
+  , modifyIORef'
+  , atomicModifyIORef'
+  , atomicWriteIORef'
+  , mkWeakIORef'
+  ) where
+
+import Control.DeepSeq
+import Control.Exception (evaluate)
+import GHC.Exts (mkWeak#)
+import GHC.IO (IO(..))
+import GHC.IORef (IORef(..))
+import GHC.STRef (STRef(..))
+import GHC.Weak (Weak(..))
+import qualified Data.IORef as Base
+
+-- | A strict (WHNF) variant of 'IORef'.
+newtype IORef' a = IORef' (Base.IORef a)
+  deriving (Eq, NFData, NFData1)
+
+-- | 'Base.newIORef' for 'IORef''.
+--
+-- Evaluates the initial value to WHNF.
+newIORef' :: a -> IO (IORef' a)
+newIORef' a = fmap IORef' . Base.newIORef =<< evaluate a
+
+-- | 'Base.readIORef' for 'IORef''.
+readIORef' :: IORef' a -> IO a
+readIORef' (IORef' var) = Base.readIORef var
+
+-- | 'Base.writeIORef' for 'IORef''.
+--
+-- Evaluates the new value to WHNF.
+writeIORef' :: IORef' a -> a -> IO ()
+writeIORef' (IORef' var) a = Base.writeIORef var =<< evaluate a
+
+-- | 'Base.modifyIORef' for 'IORef''.
+modifyIORef' :: IORef' a -> (a -> a) -> IO ()
+modifyIORef' (IORef' var) f = Base.modifyIORef' var f
+
+-- | 'Base.atomicModifyIORef' for 'IORef''.
+--
+-- Evaluates the new value to WHNF.
+atomicModifyIORef' :: IORef' a -> (a -> (a, b)) -> IO b
+atomicModifyIORef' (IORef' var) f = Base.atomicModifyIORef' var f
+
+-- | 'Base.atomicWriteIORef' for 'IORef''.
+--
+-- Evaluates the new value to WHNF.
+atomicWriteIORef' :: IORef' a -> a -> IO ()
+atomicWriteIORef' (IORef' var) a = Base.atomicWriteIORef var =<< evaluate a
+
+-- | 'Base.mkWeakIORef' for 'IORef''.
+mkWeakIORef' :: IORef' a -> IO () -> IO (Weak (IORef' a))
+mkWeakIORef' var@(IORef' (IORef (STRef var#))) (IO finalizer) = IO $ \s0 ->
+  case mkWeak# var# var finalizer s0 of
+    (# s1, w #) -> (# s1, Weak w #)
diff --git a/strict-mutable-base.cabal b/strict-mutable-base.cabal
new file mode 100644
--- /dev/null
+++ b/strict-mutable-base.cabal
@@ -0,0 +1,49 @@
+cabal-version:      3.0
+build-type:         Simple
+name:               strict-mutable-base
+version:            1.0.0.0
+homepage:           https://github.com/arybczak/strict-mutable
+license:            BSD-3-Clause
+license-file:       LICENSE
+category:           Data
+maintainer:         andrzej@rybczak.net
+author:             Andrzej Rybczak
+
+synopsis: Strict variants of mutable data types from @base@.
+
+description: Strict (WHNF) variants of @Chan@, @IORef@ and @MVar@ for proactive
+             prevention of space leaks.
+
+extra-doc-files:
+  CHANGELOG.md
+  README.md
+
+tested-with: GHC == { 8.0.2, 8.2.2, 8.4.4, 8.6.5, 8.8.4, 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.6, 9.8.2, 9.10.1 }
+
+bug-reports:   https://github.com/arybczak/strict-mutable/issues
+source-repository head
+  type:     git
+  location: https://github.com/arybczak/strict-mutable.git
+
+common language
+    ghc-options:        -Wall -Wcompat
+
+    default-language:   Haskell2010
+
+    default-extensions: GeneralizedNewtypeDeriving
+                        LambdaCase
+                        MagicHash
+                        TupleSections
+                        UnboxedTuples
+
+library
+    import:           language
+
+    build-depends:    base >=4.9 && < 5
+                    , deepseq >= 1.4.3.0
+
+    hs-source-dirs:   src
+
+    exposed-modules:  Control.Concurrent.Chan.Strict
+                      Control.Concurrent.MVar.Strict
+                      Data.IORef.Strict
