diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,11 @@
-## [*Unreleased*](https://github.com/freckle/bcp47/compare/bcp47-orphans-v0.1.1.0...main)
+## [*Unreleased*](https://github.com/freckle/bcp47/compare/bcp47-orphans-v0.1.2.0...main)
 
 None
+
+## [v0.1.2.0](https://github.com/freckle/bcp47/compare/bcp47-orphans-v0.1.1.0...bcp47-orphans-v0.1.2.0)
+
+- Add `HasCodec` instance for `BCP47`
+- Drop support GHC 8.4.
 
 ## [v0.1.1.0](https://github.com/freckle/bcp47/compare/bcp47-orphans-v0.1.0.6...bcp47-orphans-v0.1.1.0)
 
diff --git a/bcp47-orphans.cabal b/bcp47-orphans.cabal
--- a/bcp47-orphans.cabal
+++ b/bcp47-orphans.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               bcp47-orphans
-version:            0.1.1.0
+version:            0.1.2.0
 license:            MIT
 license-file:       LICENSE
 copyright:          2019 Freckle Education
@@ -23,6 +23,7 @@
 library
     exposed-modules:
         Data.BCP47.Aeson
+        Data.BCP47.Autodocodec
         Data.BCP47.Csv
         Data.BCP47.Esqueleto
         Data.BCP47.Hashable
@@ -35,17 +36,18 @@
     other-modules:    Paths_bcp47_orphans
     default-language: Haskell2010
     build-depends:
-        aeson >=1.3.1.1,
+        aeson >=1.4.6.0,
+        autodocodec >=0.2.3.0,
         base >=4.7 && <5,
         bcp47 >=0.3.0.0,
-        cassava >=0.5.1.0,
+        cassava >=0.5.2.0,
         errors >=2.3.0,
-        esqueleto >=3.4.0.1,
+        esqueleto >=3.0.0,
         hashable >=1.2.7.0,
-        http-api-data >=0.3.8.1,
+        http-api-data >=0.4.1.1,
         path-pieces >=0.2.1,
-        persistent >=2.11.0.1,
-        serialise >=0.2.6.0,
+        persistent >=2.9.2,
+        serialise >=0.2.2.0,
         text >=1.2.3.1
 
 test-suite spec
@@ -63,13 +65,13 @@
 
     default-language: Haskell2010
     build-depends:
-        QuickCheck >=2.11.3,
-        aeson >=1.3.1.1,
+        QuickCheck >=2.13.2,
+        aeson >=1.4.6.0,
         base >=4.7 && <5,
         bcp47 >=0.3.0.0,
         bcp47-orphans,
-        cassava >=0.5.1.0,
-        hspec >=2.5.5,
+        cassava >=0.5.2.0,
+        hspec >=2.7.1,
         path-pieces >=0.2.1,
-        persistent >=2.11.0.1,
-        serialise >=0.2.6.0
+        persistent >=2.9.2,
+        serialise >=0.2.2.0
diff --git a/library/Data/BCP47/Aeson.hs b/library/Data/BCP47/Aeson.hs
--- a/library/Data/BCP47/Aeson.hs
+++ b/library/Data/BCP47/Aeson.hs
@@ -1,14 +1,13 @@
+{-# LANGUAGE DerivingVia #-}
+{-# LANGUAGE StandaloneDeriving #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 module Data.BCP47.Aeson () where
 
+import Autodocodec (Autodocodec (..))
 import Data.Aeson
 import Data.BCP47
-import Data.Text (unpack)
-
-instance ToJSON BCP47 where
-  toEncoding = toEncoding . toText
-  toJSON = toJSON . toText
+import Data.BCP47.Autodocodec ()
 
-instance FromJSON BCP47 where
-  parseJSON = withText "BCP47" $ either (fail . unpack) pure . fromText
+deriving via (Autodocodec BCP47) instance FromJSON BCP47
+deriving via (Autodocodec BCP47) instance ToJSON BCP47
diff --git a/library/Data/BCP47/Autodocodec.hs b/library/Data/BCP47/Autodocodec.hs
new file mode 100644
--- /dev/null
+++ b/library/Data/BCP47/Autodocodec.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Data.BCP47.Autodocodec () where
+
+import Autodocodec
+import Data.BCP47
+import Data.Bifunctor (first)
+import Data.Text (unpack)
+
+instance HasCodec BCP47 where
+  codec =
+    bimapCodec (first unpack . fromText) toText textCodec
+      <?> "BCP47 language tag, such as 'en-US' or 'zh-Hant-TW'"
