diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# effectful-core-2.6.1.0 (2025-08-30)
+* Add `MonadError`, `MonadReader`, `MonadState` and `MonadWriter` instances for
+  `Eff` for compatibility with existing code.
+
 # effectful-core-2.6.0.0 (2025-06-13)
 * Adjust `generalBracket` with `base >= 4.21` to make use of the new exception
   annotation mechanism.
diff --git a/effectful-core.cabal b/effectful-core.cabal
--- a/effectful-core.cabal
+++ b/effectful-core.cabal
@@ -1,7 +1,7 @@
 cabal-version:      3.0
 build-type:         Simple
 name:               effectful-core
-version:            2.6.0.0
+version:            2.6.1.0
 license:            BSD-3-Clause
 license-file:       LICENSE
 category:           Control
@@ -21,7 +21,7 @@
   CHANGELOG.md
   README.md
 
-tested-with: GHC == { 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.7, 9.8.4, 9.10.2, 9.12.2 }
+tested-with: GHC == { 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.7, 9.8.4, 9.10.2, 9.12.2, 9.14.1 }
 
 bug-reports:   https://github.com/haskell-effectful/effectful/issues
 source-repository head
@@ -60,6 +60,7 @@
                         TypeApplications
                         TypeFamilies
                         TypeOperators
+                        UndecidableInstances
 
 library
     import:         language
@@ -70,6 +71,7 @@
                     , containers          >= 0.6
                     , deepseq             >= 1.2
                     , exceptions          >= 0.10.4
+                    , mtl                 >= 2.2.1
                     , monad-control       >= 1.0.3
                     , primitive           >= 0.7.3.0
                     , strict-mutable-base >= 1.1.0.0
@@ -92,6 +94,7 @@
                      Effectful.Fail
                      Effectful.Internal.Effect
                      Effectful.Internal.Env
+                     Effectful.Internal.MTL
                      Effectful.Internal.Monad
                      Effectful.Internal.Unlift
                      Effectful.Internal.Utils
diff --git a/src/Effectful.hs b/src/Effectful.hs
--- a/src/Effectful.hs
+++ b/src/Effectful.hs
@@ -64,6 +64,7 @@
 
 import Effectful.Internal.Effect
 import Effectful.Internal.Env
+import Effectful.Internal.MTL ()
 import Effectful.Internal.Monad
 
 -- $intro
diff --git a/src/Effectful/Dispatch/Dynamic.hs b/src/Effectful/Dispatch/Dynamic.hs
--- a/src/Effectful/Dispatch/Dynamic.hs
+++ b/src/Effectful/Dispatch/Dynamic.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE AllowAmbiguousTypes #-}
 {-# LANGUAGE ImplicitParams #-}
-{-# LANGUAGE UndecidableInstances #-}
 -- | Dynamically dispatched effects.
 module Effectful.Dispatch.Dynamic
   ( -- * Introduction
@@ -348,8 +347,6 @@
 -- @MonadRNG@ type class! Therefore, what we do instead is provide an
 -- __orphan__, __canonical__ instance of @MonadRNG@ for 'Eff' that delegates to
 -- the @RNG@ effect:
---
--- >>> :set -XUndecidableInstances
 --
 -- >>> :{
 --   instance RNG :> es => MonadRNG (Eff es) where
diff --git a/src/Effectful/Error/Dynamic.hs b/src/Effectful/Error/Dynamic.hs
--- a/src/Effectful/Error/Dynamic.hs
+++ b/src/Effectful/Error/Dynamic.hs
@@ -1,7 +1,8 @@
 -- | The dynamically dispatched variant of the 'Error' effect.
 --
--- /Note:/ unless you plan to change interpretations at runtime, it's
--- recommended to use the statically dispatched variant,
+-- /Note:/ unless you plan to change interpretations at runtime or you need the
+-- t'Control.Monad.Except.MonadError' instance for compatibility with existing
+-- code, it's recommended to use the statically dispatched variant,
 -- i.e. "Effectful.Error.Static".
 module Effectful.Error.Dynamic
   ( -- * Effect
@@ -33,14 +34,7 @@
 import Effectful
 import Effectful.Dispatch.Dynamic
 import Effectful.Error.Static qualified as E
-
--- | Provide the ability to handle errors of type @e@.
-data Error e :: Effect where
-  -- | @since 2.4.0.0
-  ThrowErrorWith :: (e -> String) -> e -> Error e m a
-  CatchError :: m a -> (E.CallStack -> e -> m a) -> Error e m a
-
-type instance DispatchOf (Error e) = Dynamic
+import Effectful.Internal.MTL (Error(..))
 
 -- | Handle errors of type @e@ (via "Effectful.Error.Static").
 runError
diff --git a/src/Effectful/Internal/Effect.hs b/src/Effectful/Internal/Effect.hs
--- a/src/Effectful/Internal/Effect.hs
+++ b/src/Effectful/Internal/Effect.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE AllowAmbiguousTypes #-}
-{-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_HADDOCK not-home #-}
 -- | Type-safe indexing for 'Effectful.Internal.Monad.Env'.
 --
diff --git a/src/Effectful/Internal/MTL.hs b/src/Effectful/Internal/MTL.hs
new file mode 100644
--- /dev/null
+++ b/src/Effectful/Internal/MTL.hs
@@ -0,0 +1,90 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+-- | Definitions and instances for MTL compatibility.
+--
+-- This module is intended for internal use only, and may change without warning
+-- in subsequent releases.
+module Effectful.Internal.MTL where
+
+import Control.Monad.Except qualified as MTL
+import Control.Monad.Reader qualified as MTL
+import Control.Monad.State qualified as MTL
+import Control.Monad.Writer qualified as MTL
+import GHC.Stack (CallStack)
+
+import Effectful.Internal.Effect
+import Effectful.Internal.Env
+import Effectful.Internal.Monad
+
+-- | Provide the ability to handle errors of type @e@.
+data Error e :: Effect where
+  -- | @since 2.4.0.0
+  ThrowErrorWith :: (e -> String) -> e -> Error e m a
+  CatchError :: m a -> (CallStack -> e -> m a) -> Error e m a
+
+type instance DispatchOf (Error e) = Dynamic
+
+-- | Instance included for compatibility with existing code.
+instance
+  ( Show e
+  , Error e :> es
+  , MTL.MonadError e (Eff es)
+  ) => MTL.MonadError e (Eff es) where
+  throwError = send . ThrowErrorWith show
+  catchError action = send . CatchError action . const
+
+----------------------------------------
+
+data Reader r :: Effect where
+  Ask   :: Reader r m r
+  Local :: (r -> r) -> m a -> Reader r m a
+
+type instance DispatchOf (Reader r) = Dynamic
+
+-- | Instance included for compatibility with existing code.
+instance
+  ( Reader r :> es
+  , MTL.MonadReader r (Eff es)
+  ) => MTL.MonadReader r (Eff es) where
+  ask = send Ask
+  local f = send . Local f
+  reader f = f <$> send Ask
+
+----------------------------------------
+
+-- | Provide access to a mutable value of type @s@.
+data State s :: Effect where
+  Get    :: State s m s
+  Put    :: s -> State s m ()
+  State  :: (s ->   (a, s)) -> State s m a
+  StateM :: (s -> m (a, s)) -> State s m a
+
+type instance DispatchOf (State s) = Dynamic
+
+-- | Instance included for compatibility with existing code.
+instance
+  ( State s :> es
+  , MTL.MonadState s (Eff es)
+  ) => MTL.MonadState s (Eff es) where
+  get = send Get
+  put = send . Put
+  state = send . State
+
+----------------------------------------
+
+-- | Provide access to a write only value of type @w@.
+data Writer w :: Effect where
+  Tell   :: w   -> Writer w m ()
+  Listen :: m a -> Writer w m (a, w)
+
+type instance DispatchOf (Writer w) = Dynamic
+
+-- | Instance included for compatibility with existing code.
+instance
+  ( Monoid w
+  , Writer w :> es
+  , MTL.MonadWriter w (Eff es)
+  ) => MTL.MonadWriter w (Eff es) where
+  writer (a, w) = a <$ send (Tell w)
+  tell = send . Tell
+  listen = send . Listen
+  pass = error "pass is not implemented due to ambiguous semantics in presence of runtime exceptions"
diff --git a/src/Effectful/Internal/Monad.hs b/src/Effectful/Internal/Monad.hs
--- a/src/Effectful/Internal/Monad.hs
+++ b/src/Effectful/Internal/Monad.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_GHC -Wno-orphans #-}
 {-# OPTIONS_HADDOCK not-home #-}
 -- | The 'Eff' monad.
diff --git a/src/Effectful/Reader/Dynamic.hs b/src/Effectful/Reader/Dynamic.hs
--- a/src/Effectful/Reader/Dynamic.hs
+++ b/src/Effectful/Reader/Dynamic.hs
@@ -1,7 +1,8 @@
 -- | The dynamically dispatched variant of the 'Reader' effect.
 --
--- /Note:/ unless you plan to change interpretations at runtime, it's
--- recommended to use the statically dispatched variant,
+-- /Note:/ unless you plan to change interpretations at runtime or you need the
+-- t'Control.Monad.Reader.MonadReader' instance for compatibility with existing
+-- code, it's recommended to use the statically dispatched variant,
 -- i.e. "Effectful.Reader.Static".
 module Effectful.Reader.Dynamic
   ( -- * Effect
@@ -19,12 +20,7 @@
 
 import Effectful
 import Effectful.Dispatch.Dynamic
-
-data Reader r :: Effect where
-  Ask   :: Reader r m r
-  Local :: (r -> r) -> m a -> Reader r m a
-
-type instance DispatchOf (Reader r) = Dynamic
+import Effectful.Internal.MTL (Reader(..))
 
 -- | Run the 'Reader' effect with the given initial environment.
 runReader
diff --git a/src/Effectful/State/Dynamic.hs b/src/Effectful/State/Dynamic.hs
--- a/src/Effectful/State/Dynamic.hs
+++ b/src/Effectful/State/Dynamic.hs
@@ -1,7 +1,8 @@
 -- | The dynamically dispatched variant of the 'State' effect.
 --
--- /Note:/ unless you plan to change interpretations at runtime, it's
--- recommended to use one of the statically dispatched variants,
+-- /Note:/ unless you plan to change interpretations at runtime or you need the
+-- t'Control.Monad.State.MonadState' instance for compatibility with existing
+-- code, it's recommended to use one of the statically dispatched variants,
 -- i.e. "Effectful.State.Static.Local" or "Effectful.State.Static.Shared".
 module Effectful.State.Dynamic
   ( -- * Effect
@@ -31,17 +32,9 @@
 
 import Effectful
 import Effectful.Dispatch.Dynamic
+import Effectful.Internal.MTL (State(..))
 import Effectful.State.Static.Local qualified as L
 import Effectful.State.Static.Shared qualified as S
-
--- | Provide access to a mutable value of type @s@.
-data State s :: Effect where
-  Get    :: State s m s
-  Put    :: s -> State s m ()
-  State  :: (s ->   (a, s)) -> State s m a
-  StateM :: (s -> m (a, s)) -> State s m a
-
-type instance DispatchOf (State s) = Dynamic
 
 ----------------------------------------
 -- Local
diff --git a/src/Effectful/Writer/Dynamic.hs b/src/Effectful/Writer/Dynamic.hs
--- a/src/Effectful/Writer/Dynamic.hs
+++ b/src/Effectful/Writer/Dynamic.hs
@@ -1,7 +1,9 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
 -- | The dynamically dispatched variant of the 'Writer' effect.
 --
--- /Note:/ unless you plan to change interpretations at runtime, it's
--- recommended to use one of the statically dispatched variants,
+-- /Note:/ unless you plan to change interpretations at runtime or you need the
+-- t'Control.Monad.Writer.MonadWriter' instance for compatibility with existing
+-- code, it's recommended to use one of the statically dispatched variants,
 -- i.e. "Effectful.Writer.Static.Local" or "Effectful.Writer.Static.Shared".
 module Effectful.Writer.Dynamic
   ( -- * Effect
@@ -25,15 +27,9 @@
 
 import Effectful
 import Effectful.Dispatch.Dynamic
+import Effectful.Internal.MTL (Writer(..))
 import Effectful.Writer.Static.Local qualified as L
 import Effectful.Writer.Static.Shared qualified as S
-
--- | Provide access to a write only value of type @w@.
-data Writer w :: Effect where
-  Tell   :: w   -> Writer w m ()
-  Listen :: m a -> Writer w m (a, w)
-
-type instance DispatchOf (Writer w) = Dynamic
 
 ----------------------------------------
 -- Local
