diff --git a/composite-aeson.cabal b/composite-aeson.cabal
--- a/composite-aeson.cabal
+++ b/composite-aeson.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           composite-aeson
-version:        0.3.1.0
+version:        0.4.0.0
 synopsis:       JSON for Vinyl/Frames records
 description:    Integration between Aeson and Vinyl/Frames records allowing records to be easily converted to JSON using automatic derivation, explicit formats, or a mix of both.
 category:       Records
@@ -18,7 +18,7 @@
 library
   hs-source-dirs:
       src
-  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeFamilies TypeOperators ViewPatterns
+  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators ViewPatterns
   ghc-options: -Wall -O2
   build-depends:
       base >= 4.7 && < 5
@@ -42,6 +42,7 @@
   exposed-modules:
       Composite.Aeson
       Composite.Aeson.Base
+      Composite.Aeson.CoRecord
       Composite.Aeson.DateTimeFormatUtils
       Composite.Aeson.Enum
       Composite.Aeson.Formats.DateTime
@@ -58,7 +59,7 @@
   main-is: Main.hs
   hs-source-dirs:
       test
-  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeFamilies TypeOperators ViewPatterns
+  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators ViewPatterns
   ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N -fno-warn-orphans
   build-depends:
       base >= 4.7 && < 5
diff --git a/src/Composite/Aeson/CoRecord.hs b/src/Composite/Aeson/CoRecord.hs
new file mode 100644
--- /dev/null
+++ b/src/Composite/Aeson/CoRecord.hs
@@ -0,0 +1,62 @@
+module Composite.Aeson.CoRecord
+  ( JsonFormatField, DefaultJsonFormatField(defaultJsonFormatField)
+  , fieldJsonFormat
+  ) where
+
+import Composite.Aeson.Base (FromJson(FromJson), JsonFormat(JsonFormat), JsonProfunctor(JsonProfunctor), wrappedJsonFormat)
+import Composite.Aeson.Formats.Default (DefaultJsonFormat, defaultJsonFormat)
+import Composite.Aeson.Formats.Generic (SumStyle, jsonSumFormat)
+import Composite.CoRecord (CoRec(CoVal), Field, fieldToRec)
+import Composite.Record ((:->), Rec((:&), RNil), RecWithContext(rmapWithContext), recordToNonEmpty, ReifyNames, reifyNames)
+import Data.Aeson (Value)
+import Data.Functor.Identity (Identity(Identity))
+import Data.List.NonEmpty (NonEmpty)
+import Data.Maybe (catMaybes, fromMaybe, listToMaybe)
+import Data.Text (Text)
+import Data.Vinyl (RecApplicative, rapply, recordToList, (<<&>>))
+import Data.Vinyl.Functor (Compose(Compose), (:.), Const(Const), Lift(Lift))
+import Data.Vinyl.Lens (type (∈))
+import Data.Proxy (Proxy(Proxy))
+
+-- |Type of records which contain JSON formats for each element of @rs@.
+type JsonFormatField e rs = Rec (JsonFormat e) rs
+
+-- |Class which makes up a 'JsonFormatField' for some @rs@ where each @r ~ s :-> a@ by using the 'DefaultJsonFormat' instance for each @a@.
+class DefaultJsonFormatField (rs :: [*]) where
+  -- |Make up a 'JsonFormatField' for some @rs@ where each @r ~ s :-> a@ by using the 'DefaultJsonFormat' instance for each @a@.
+  defaultJsonFormatField :: JsonFormatField e rs
+
+instance DefaultJsonFormatField '[] where
+  defaultJsonFormatField = RNil
+
+instance forall s a rs. (DefaultJsonFormat a, DefaultJsonFormatField rs) => DefaultJsonFormatField (s :-> a ': rs) where
+  defaultJsonFormatField = wrappedJsonFormat defaultJsonFormat :& (defaultJsonFormatField :: JsonFormatField e rs)
+
+-- |Make a @'JsonFormat' e (Field rs)@ given how to map the sum type to JSON alogn with a record with formatters for each value the field could have.
+fieldJsonFormat
+  :: forall (rs :: [*]) r' (rs' :: [*]) e.
+     (rs ~ (r' ': rs'), RecApplicative rs, RecWithContext rs rs, ReifyNames rs)
+  => SumStyle -> JsonFormatField e rs -> JsonFormat e (Field rs)
+fieldJsonFormat sumStyle fmts = jsonSumFormat sumStyle o i
+  where
+    namedFmts :: Rec ((,) Text :. JsonFormat e) rs
+    namedFmts = reifyNames fmts
+
+    o :: Field rs -> (Text, Value)
+    o = fromMaybe (error "fieldToRec somehow produced all Nothings")
+      . listToMaybe . catMaybes
+      . (recordToList :: Rec (Const (Maybe (Text, Value))) rs -> [Maybe (Text, Value)])
+      . rapply outputs
+      . fieldToRec
+
+    outputs :: Rec (Lift (->) Maybe (Const (Maybe (Text, Value)))) rs
+    outputs = namedFmts <<&>> \ (Compose (name, JsonFormat (JsonProfunctor oa _))) ->
+      Lift $ Const . fmap ((name,) . oa)
+
+    i :: NonEmpty (Text, FromJson e (Field rs))
+    i = recordToNonEmpty $ rmapWithContext (Proxy @rs) oneCase namedFmts
+      where
+        oneCase :: forall r. r ∈ rs => ((,) Text :. JsonFormat e) r -> Const (Text, FromJson e (Field rs)) r
+        oneCase (Compose (name, JsonFormat (JsonProfunctor _ ia))) =
+          Const (name, FromJson (CoVal . Identity <$> ia))
+
