diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,41 @@
+# 1.2
+
+Release corresponding to [`lens-aeson-1.2`](https://hackage.haskell.org/package/lens-aeson-1.2)
+API changes.
+
+* Require `aeson-2.0.3.*` and `optics-core-0.4.1` or greater.
+* Drop support for GHC-8.0
+* Change the types of `_Object`, `key`, and `members`:
+
+  ```diff
+  -_Object :: Prism' t (HashMap Text Value)
+  +_Object :: Prism' t (KeyMap Value)
+
+  -key :: AsValue t => Text -> AffineTraversal' t Value
+  +key :: AsValue t => Key  -> AffineTraversal' t Value
+
+  -members :: AsValue t => IndexedTraversal' Text t Value
+  +members :: AsValue t => IndexedTraversal' Key  t Value
+  ```
+
+  This mirrors similar changes made in `aeson-2.0.*`, where the type of
+  `Object`'s field was changed from `HashMap Text Value` to `KeyMap Value`.
+
+  The `Ixed Value` instance changes similarly:
+
+  ```diff
+  -type instance Index Value = Text
+  +type instance Index Value = Key
+  ```
+* Remove `Primitive` and `AsPrimitive`, since https://tools.ietf.org/html/rfc7159
+  de-emphasized the notion of primitive versus composite JSON values.
+  * The `AsPrimitive` methods (`_Value`, `_String`, and `_Bool`) are now
+    `AsValue` methods.
+  * `_Number`'s default signature, `Bool_`, `String_`, and `Null_` now have an
+    `AsValue` constraint.
+* Add an `IsKey` class, whose method `_Key` is an `Iso` for converting values
+  to and from a `Key`.
+
 # 1.1.1
 
 - Support `aeson-2.0.0.0`: add instances for `KeyMap`.
diff --git a/aeson-optics.cabal b/aeson-optics.cabal
--- a/aeson-optics.cabal
+++ b/aeson-optics.cabal
@@ -1,4 +1,4 @@
-version:            1.1.1
+version:            1.2
 name:               aeson-optics
 category:           Data, JSON, Optics
 license:            MIT
@@ -16,14 +16,20 @@
 
 build-type:         Simple
 tested-with:
-  GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1
+  GHC ==8.2.2
+   || ==8.4.4
+   || ==8.6.5
+   || ==8.8.4
+   || ==8.10.7
+   || ==9.0.2
+   || ==9.2.2
 
 synopsis:           Law-abiding optics for aeson
 description:        Law-abiding optics for aeson.
 extra-source-files:
   AUTHORS.markdown
-  README.markdown
   CHANGELOG.md
+  README.markdown
 
 source-repository head
   type:     git
@@ -32,15 +38,15 @@
 library
   default-language: Haskell2010
   build-depends:
-      aeson                 >=0.11     && <1.6 || >=2.0 && <2.1
+      aeson                 >=2.0.3.0  && <2.1
     , attoparsec            >=0.13.1.0 && <0.15
-    , base                  >=4.9      && <4.16
-    , base-compat           >=0.9.3    && <0.13
+    , base                  >=4.10     && <4.17
     , bytestring            >=0.10.8.1 && <0.12
-    , optics-core           >=0.1      && <0.5
-    , optics-extra          >=0.1      && <0.5
+    , optics-core           >=0.4.1    && <0.5
+    , optics-extra          >=0.4.1    && <0.5
     , scientific            >=0.3.4.9  && <0.4
-    , text                  >=1.2.2.0  && <1.3
+    , text-short            >=0.1.5    && <0.2
+    , text                  >=1.2.2.0  && <1.3 || >=2.0 && <2.1
     , unordered-containers  >=0.2.8.0  && <0.3
     , vector                >=0.11     && <0.13
 
diff --git a/src/Data/Aeson/Optics.hs b/src/Data/Aeson/Optics.hs
--- a/src/Data/Aeson/Optics.hs
+++ b/src/Data/Aeson/Optics.hs
@@ -27,13 +27,11 @@
     AsNumber(..)
   , _Integral
   , nonNull
-  -- * Primitive
-  , Primitive(..)
-  , AsPrimitive(..)
   -- * Objects and Arrays
   , AsValue(..)
   , key, members
   , nth, values
+  , IsKey (..)
   -- * Decoding
   , AsJSON(..)
   , _JSON'
@@ -44,48 +42,48 @@
   , pattern Double
   , pattern Integer
   , pattern Integral
-  , pattern Primitive
   , pattern Bool_
   , pattern String_
   , pattern Null_
+  , pattern Key_
   ) where
 
-import Prelude ()
-import Prelude.Compat hiding (null)
+import Prelude hiding (null)
 
 import Data.Aeson
        (FromJSON, Result (..), ToJSON, Value (..), encode, fromJSON, toJSON)
 import Data.Aeson.Parser               (value)
 import Data.Attoparsec.ByteString.Lazy (maybeResult, parse)
-import Data.ByteString.Lazy.Char8      as Lazy hiding (putStrLn)
-import Data.Data
-import Data.HashMap.Strict             (HashMap)
 import Data.Scientific                 (Scientific)
 import Data.Text                       (Text)
 import Data.Text.Optics                (packed)
+import Data.Text.Short                 (ShortText)
 import Data.Vector                     (Vector)
 
 import Optics.At ()
 import Optics.Core
 import Optics.Indexed ()
 
-import qualified Data.ByteString         as Strict
-import qualified Data.ByteString.Lazy    as LBS
-import qualified Data.Scientific         as Scientific
-import qualified Data.Text               as StrictText
-import qualified Data.Text.Encoding      as StrictText
-import qualified Data.Text.Lazy          as LazyText
-import qualified Data.Text.Lazy.Encoding as LazyText
-
-#if MIN_VERSION_aeson(2,0,0)
-import qualified Data.Aeson.Key    as Key
-import qualified Data.Aeson.KeyMap as KM
-#endif
+import qualified Data.Aeson.Key             as Key
+import qualified Data.Aeson.KeyMap          as KM
+import qualified Data.ByteString            as Strict
+import qualified Data.ByteString.Lazy       as LBS
+import qualified Data.Scientific            as Scientific
+import qualified Data.Text                  as StrictText
+import qualified Data.Text.Encoding         as StrictText
+import qualified Data.Text.Lazy             as LazyText
+import qualified Data.Text.Lazy.Encoding    as LazyText
 
 -- $setup
--- >>> import Data.ByteString.Char8 as Strict.Char8
--- >>> import qualified Data.Vector as Vector
--- >>> import qualified Data.HashMap.Strict as HashMap
+-- >>> import Optics.Core
+-- >>> import Data.Aeson (Value (..))
+-- >>> import Data.Text (Text)
+-- >>> import qualified Data.ByteString             as Strict
+-- >>> import qualified Data.ByteString.Char8       as Strict.Char8
+-- >>> import qualified Data.ByteString.Lazy        as Lazy
+-- >>> import qualified Data.ByteString.Lazy.Char8  as Lazy.Char8
+-- >>> import qualified Data.Aeson.KeyMap           as KeyMap
+-- >>> import qualified Data.Vector                 as Vector
 -- >>> :set -XOverloadedStrings
 -- >>> import Optics.Operators
 
@@ -101,8 +99,8 @@
   -- >>> "[1, \"x\"]" ^? nth 1 % _Number
   -- Nothing
   _Number :: Prism' t Scientific
-  default _Number :: AsPrimitive t => Prism' t Scientific
-  _Number = _Primitive%_Number
+  default _Number :: AsValue t => Prism' t Scientific
+  _Number = _Value%_Number
   {-# INLINE _Number #-}
 
   -- |
@@ -138,7 +136,7 @@
   {-# INLINE _Number #-}
 
 instance AsNumber Strict.ByteString
-instance AsNumber Lazy.ByteString
+instance AsNumber LBS.ByteString
 instance AsNumber Text
 instance AsNumber LazyText.Text
 instance AsNumber String
@@ -158,54 +156,60 @@
 _Integral = _Number % iso floor fromIntegral
 {-# INLINE _Integral #-}
 
+-- | Prism into non-'Null' values
+--
+-- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "a" % nonNull
+-- Just (String "xyz")
+--
+-- >>> "{\"a\": {}, \"b\": null}" ^? key "a" % nonNull
+-- Just (Object (fromList []))
+--
+-- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "b" % nonNull
+-- Nothing
+nonNull :: Prism' Value Value
+nonNull = prism id (\v -> if isn't _Null v then Right v else Left v)
+{-# INLINE nonNull #-}
+
 ------------------------------------------------------------------------------
--- Null values and primitives
+-- Non-primitive traversals
 ------------------------------------------------------------------------------
 
--- | Primitives of 'Value'
-data Primitive
-  = StringPrim !Text
-  | NumberPrim !Scientific
-  | BoolPrim !Bool
-  | NullPrim
-  deriving (Eq,Ord,Show,Data,Typeable)
-
-instance AsNumber Primitive where
-  _Number = prism NumberPrim $ \v -> case v of NumberPrim s -> Right s; _ -> Left v
-  {-# INLINE _Number #-}
+class AsNumber t => AsValue t where
+  -- |
+  -- >>> preview _Value "[1,2,3]" == Just (Array (Vector.fromList [Number 1.0,Number 2.0,Number 3.0]))
+  -- True
+  _Value :: Prism' t Value
 
-class AsNumber t => AsPrimitive t where
   -- |
-  -- >>> "[1, \"x\", null, true, false]" ^? nth 0 % _Primitive
-  -- Just (NumberPrim 1.0)
-  --
-  -- >>> "[1, \"x\", null, true, false]" ^? nth 1 % _Primitive
-  -- Just (StringPrim "x")
-  --
-  -- >>> "[1, \"x\", null, true, false]" ^? nth 2 % _Primitive
-  -- Just NullPrim
+  -- >>> "{\"a\": {}, \"b\": null}" ^? key "a" % _Object
+  -- Just (fromList [])
   --
-  -- >>> "[1, \"x\", null, true, false]" ^? nth 3 % _Primitive
-  -- Just (BoolPrim True)
+  -- >>> "{\"a\": {}, \"b\": null}" ^? key "b" % _Object
+  -- Nothing
   --
-  -- >>> "[1, \"x\", null, true, false]" ^? nth 4 % _Primitive
-  -- Just (BoolPrim False)
-  _Primitive :: Prism' t Primitive
-  default _Primitive :: AsValue t => Prism' t Primitive
-  _Primitive = _Value%_Primitive
-  {-# INLINE _Primitive #-}
+  -- >>> _Object # KeyMap.fromList [("key", _String # "value")] :: String
+  -- "{\"key\":\"value\"}"
+  _Object :: Prism' t (KM.KeyMap Value)
+  _Object = _Value%prism Object  (\v -> case v of Object o -> Right o; _ -> Left v)
 
   -- |
+  -- >>> preview _Array "[1,2,3]" == Just (Vector.fromList [Number 1.0,Number 2.0,Number 3.0])
+  -- True
+  _Array :: Prism' t (Vector Value)
+  _Array = _Value%prism Array (\v -> case v of Array a -> Right a; _ -> Left v)
+  {-# INLINE _Array #-}
+
+  -- |
   -- >>> "{\"a\": \"xyz\", \"b\": true}" ^? key "a" % _String
   -- Just "xyz"
   --
   -- >>> "{\"a\": \"xyz\", \"b\": true}" ^? key "b" % _String
   -- Nothing
   --
-  -- >>> _Object # HashMap.fromList [("key", _String # "value")] :: String
+  -- >>> _Object # KeyMap.fromList [("key", _String # "value")] :: String
   -- "{\"key\":\"value\"}"
   _String :: Prism' t Text
-  _String = _Primitive%prism StringPrim (\v -> case v of StringPrim s -> Right s; _ -> Left v)
+  _String = _Value%prism String (\v -> case v of String s -> Right s; _ -> Left v)
   {-# INLINE _String #-}
 
   -- |
@@ -221,7 +225,7 @@
   -- >>> _Bool # False :: String
   -- "false"
   _Bool :: Prism' t Bool
-  _Bool = _Primitive%prism BoolPrim (\v -> case v of BoolPrim b -> Right b; _ -> Left v)
+  _Bool = _Value%prism Bool (\v -> case v of Bool b -> Right b; _ -> Left v)
   {-# INLINE _Bool #-}
 
   -- |
@@ -234,94 +238,10 @@
   -- >>> _Null # () :: String
   -- "null"
   _Null :: Prism' t ()
-  _Null = _Primitive % prism (const NullPrim) (\v -> case v of NullPrim -> Right (); _ -> Left v)
+  _Null = _Value % prism (const Null) (\v -> case v of Null -> Right (); _ -> Left v)
   {-# INLINE _Null #-}
 
 
-instance AsPrimitive Value where
-  _Primitive = prism fromPrim toPrim
-    where
-      toPrim (String s) = Right $ StringPrim s
-      toPrim (Number n) = Right $ NumberPrim n
-      toPrim (Bool b)   = Right $ BoolPrim b
-      toPrim Null       = Right NullPrim
-      toPrim v          = Left v
-      {-# INLINE toPrim #-}
-      fromPrim (StringPrim s) = String s
-      fromPrim (NumberPrim n) = Number n
-      fromPrim (BoolPrim b)   = Bool b
-      fromPrim NullPrim       = Null
-      {-# INLINE fromPrim #-}
-  {-# INLINE _Primitive #-}
-  _String = prism String $ \v -> case v of String s -> Right s; _ -> Left v
-  {-# INLINE _String #-}
-  _Bool = prism Bool (\v -> case v of Bool b -> Right b; _ -> Left v)
-  {-# INLINE _Bool #-}
-  _Null = prism (const Null) (\v -> case v of Null -> Right (); _ -> Left v)
-  {-# INLINE _Null #-}
-
-instance AsPrimitive Strict.ByteString
-instance AsPrimitive Lazy.ByteString
-instance AsPrimitive StrictText.Text
-instance AsPrimitive LazyText.Text
-instance AsPrimitive String
-
-instance AsPrimitive Primitive where
-  _Primitive = castOptic simple
-  {-# INLINE _Primitive #-}
-
--- | Prism into non-'Null' values
---
--- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "a" % nonNull
--- Just (String "xyz")
---
--- >>> "{\"a\": {}, \"b\": null}" ^? key "a" % nonNull
--- Just (Object (fromList []))
---
--- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "b" % nonNull
--- Nothing
-nonNull :: Prism' Value Value
-nonNull = prism id (\v -> if isn't _Null v then Right v else Left v)
-{-# INLINE nonNull #-}
-
-------------------------------------------------------------------------------
--- Non-primitive traversals
-------------------------------------------------------------------------------
-
-class AsPrimitive t => AsValue t where
-  -- |
-  -- >>> preview _Value "[1,2,3]" == Just (Array (Vector.fromList [Number 1.0,Number 2.0,Number 3.0]))
-  -- True
-  _Value :: Prism' t Value
-
-  -- |
-  -- >>> "{\"a\": {}, \"b\": null}" ^? key "a" % _Object
-  -- Just (fromList [])
-  --
-  -- >>> "{\"a\": {}, \"b\": null}" ^? key "b" % _Object
-  -- Nothing
-  --
-  -- >>> _Object # HashMap.fromList [("key", _String # "value")] :: String
-  -- "{\"key\":\"value\"}"
-  _Object :: Prism' t (HashMap Text Value)
-  _Object = _Value%prism (Object . fwd) (\v -> case v of Object o -> Right (bwd o); _ -> Left v)
-    where
-#if MIN_VERSION_aeson(2,0,0)
-    fwd = KM.fromHashMapText
-    bwd = KM.toHashMapText
-#else
-    fwd = id
-    bwd = id
-#endif
-  {-# INLINE _Object #-}
-
-  -- |
-  -- >>> preview _Array "[1,2,3]" == Just (Vector.fromList [Number 1.0,Number 2.0,Number 3.0])
-  -- True
-  _Array :: Prism' t (Vector Value)
-  _Array = _Value%prism Array (\v -> case v of Array a -> Right a; _ -> Left v)
-  {-# INLINE _Array #-}
-
 instance AsValue Value where
   _Value = castOptic simple
   {-# INLINE _Value #-}
@@ -330,7 +250,7 @@
   _Value = _JSON
   {-# INLINE _Value #-}
 
-instance AsValue Lazy.ByteString where
+instance AsValue LBS.ByteString where
   _Value = _JSON
   {-# INLINE _Value #-}
 
@@ -355,7 +275,7 @@
 --
 -- >>> "[1,2,3]" ^? key "a"
 -- Nothing
-key :: AsValue t => Text -> AffineTraversal' t Value
+key :: AsValue t => Key.Key -> AffineTraversal' t Value
 key i = _Object % ix i
 {-# INLINE key #-}
 
@@ -366,7 +286,7 @@
 --
 -- >>> "{\"a\": 4}" & members % _Number %~ (*10)
 -- "{\"a\":40}"
-members :: AsValue t => IxTraversal' Text t Value
+members :: AsValue t => IxTraversal' Key.Key t Value
 members = _Object % itraversed
 {-# INLINE members #-}
 
@@ -401,12 +321,57 @@
 strictTextUtf8 :: Iso' StrictText.Text Strict.ByteString
 strictTextUtf8 = iso StrictText.encodeUtf8 StrictText.decodeUtf8
 
-lazyTextUtf8 :: Iso' LazyText.Text Lazy.ByteString
+lazyTextUtf8 :: Iso' LazyText.Text LBS.ByteString
 lazyTextUtf8 = iso LazyText.encodeUtf8 LazyText.decodeUtf8
 
 _JSON' :: (AsJSON t, FromJSON a, ToJSON a) => Prism' t a
 _JSON' = _JSON
 
+class IsKey t where
+  -- | '_Key' is an 'Iso' from something to a 'Key'. This is primarily intended
+  -- for situations where one wishes to use object keys that are not string
+  -- literals and therefore must be converted:
+  --
+  -- >>> let k = "a" :: Text
+  -- >>> "{\"a\": 100, \"b\": 200}" ^? key (k ^. _Key)
+  -- Just (Number 100.0)
+  --
+  -- Note that applying '_Key' directly to a string literal
+  -- (e.g., @\"a\" ^. '_Key'@) will likely not typecheck when
+  -- @OverloadedStrings@ is enabled.
+  _Key :: Iso' t Key.Key
+
+instance IsKey Key.Key where
+  _Key = simple
+  {-# INLINE _Key #-}
+
+instance IsKey String where
+  _Key = iso Key.fromString Key.toString
+  {-# INLINE _Key #-}
+
+instance IsKey Text where
+  _Key = iso Key.fromText Key.toText
+  {-# INLINE _Key #-}
+
+instance IsKey LazyText.Text where
+  _Key = iso LazyText.toStrict LazyText.fromStrict % _Key
+  {-# INLINE _Key #-}
+
+instance IsKey ShortText where
+  _Key = iso Key.fromShortText Key.toShortText
+  {-# INLINE _Key #-}
+
+{-
+https://github.com/lens/lens-aeson/issues/48
+instance IsKey Strict.ByteString where
+  _Key = from strictTextUtf8._Key
+  {-# INLINE _Key #-}
+
+instance IsKey LBS.ByteString where
+  _Key = from lazyTextUtf8._Key
+  {-# INLINE _Key #-}
+-}
+
 class AsJSON t where
   -- | '_JSON' is a 'Prism' from something containing JSON to something encoded in that structure
   _JSON :: (FromJSON a, ToJSON b) => Prism t t a b
@@ -415,10 +380,10 @@
   _JSON = iso LBS.fromStrict LBS.toStrict % _JSON
   {-# INLINE _JSON #-}
 
-instance AsJSON Lazy.ByteString where
+instance AsJSON LBS.ByteString where
   _JSON = prism' encode decodeValue
     where
-      decodeValue :: (FromJSON a) => Lazy.ByteString -> Maybe a
+      decodeValue :: (FromJSON a) => LBS.ByteString -> Maybe a
       decodeValue s = maybeResult (parse value s) >>= \x -> case fromJSON x of
         Success v -> Just v
         _         -> Nothing
@@ -453,7 +418,7 @@
 -- >>> preview (_Integer :: Prism' Lazy.ByteString Integer) "42"
 -- Just 42
 --
--- >>> Lazy.unpack (review (_Integer :: Prism' Lazy.ByteString Integer) 42)
+-- >>> Lazy.Char8.unpack (review (_Integer :: Prism' Lazy.ByteString Integer) 42)
 -- "42"
 
 -- $StrictByteStringTests
@@ -480,7 +445,7 @@
 -- Orphan instances for lens library interop
 ------------------------------------------------------------------------------
 
-type instance Index Value = Text
+type instance Index Value = Key.Key
 
 type instance IxValue Value = Value
 instance Ixed Value where
@@ -495,7 +460,6 @@
   {-# INLINE plate #-}
 -}
 
-#if MIN_VERSION_aeson(2,0,0)
 type instance Index (KM.KeyMap v) = Key.Key
 type instance IxValue (KM.KeyMap v) = v
 
@@ -508,13 +472,11 @@
 instance Each Key.Key (KM.KeyMap a) (KM.KeyMap b) a b where
   each = itraversalVL KM.traverseWithKey
   {-# INLINE[1] each #-}
-#endif
 
 ------------------------------------------------------------------------------
 -- Pattern Synonyms
 ------------------------------------------------------------------------------
 
-#if __GLASGOW_HASKELL__ >= 800
 pattern JSON :: (FromJSON a, ToJSON a, AsJSON t) => () => a -> t
 pattern JSON a <- (preview _JSON -> Just a) where
   JSON a = _JSON # a
@@ -539,19 +501,18 @@
 pattern Integral d <- (preview _Integral -> Just d) where
   Integral d = _Integral # d
 
-pattern Primitive :: AsPrimitive t => Primitive -> t
-pattern Primitive p <- (preview _Primitive -> Just p) where
-  Primitive p = _Primitive # p
-
-pattern Bool_ :: AsPrimitive t => Bool -> t
+pattern Bool_ :: AsValue t => Bool -> t
 pattern Bool_ b <- (preview _Bool -> Just b) where
   Bool_ b = _Bool # b
 
-pattern String_ :: AsPrimitive t => Text -> t
+pattern String_ :: AsValue t => Text -> t
 pattern String_ p <- (preview _String -> Just p) where
   String_ p = _String # p
 
-pattern Null_ :: AsPrimitive t => t
+pattern Null_ :: AsValue t => t
 pattern Null_ <- (preview _Null -> Just ()) where
   Null_ = _Null # ()
-#endif
+
+pattern Key_ :: IsKey t => Key.Key -> t
+pattern Key_ k <- (preview _Key -> Just k) where
+  Key_ k = _Key # k
