diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,37 @@
+# ChangeLog for file-embed
+
+## 0.0.16.0
+
+* Add `embedFileRelative`
+
+## 0.0.15.0
+
+* Add `makeRelativeToLocationPredicate`
+
+## 0.0.14.0
+
+* Add `embedFileIfExists`
+
+## 0.0.13.0
+
+* Ensure that directory listings are returned in sorted order for reproducibility [yesodweb/yesod#1684](https://github.com/yesodweb/yesod/issues/1684)
+
+## 0.0.12.0
+
+* Use the `Bytes` literal on newer GHCs to reduce memory usage during compilation [#36](https://github.com/snoyberg/file-embed/pull/36)
+
+## 0.0.11.2
+
+* Haddock markup fix
+
+## 0.0.11.1
+
+* Support GHC 8.10
+
+## 0.0.11
+
+* embedDirListing [#26](https://github.com/snoyberg/file-embed/pull/26)
+
 ## 0.0.10.1
 
 * Minor doc improvements
diff --git a/Data/FileEmbed.hs b/Data/FileEmbed.hs
--- a/Data/FileEmbed.hs
+++ b/Data/FileEmbed.hs
@@ -19,8 +19,11 @@
 module Data.FileEmbed
     ( -- * Embed at compile time
       embedFile
+    , embedFileRelative
+    , embedFileIfExists
     , embedOneFileOf
     , embedDir
+    , embedDirListing
     , getDir
       -- * Embed as a IsString
     , embedStringFile
@@ -37,6 +40,7 @@
     , injectFileWith
       -- * Relative path manipulation
     , makeRelativeToProject
+    , makeRelativeToLocationPredicate
       -- * Internal
     , stringToBs
     , bsToExp
@@ -45,11 +49,7 @@
 
 import Language.Haskell.TH.Syntax
     ( Exp (AppE, ListE, LitE, TupE, SigE, VarE)
-#if MIN_VERSION_template_haskell(2,5,0)
-    , Lit (StringL, StringPrimL, IntegerL)
-#else
-    , Lit (StringL, IntegerL)
-#endif
+    , Lit (..)
     , Q
     , runIO
     , qLocation, loc_filename
@@ -57,19 +57,26 @@
     , Quasi(qAddDependentFile)
 #endif
     )
+#if MIN_VERSION_template_haskell(2,16,0)
+import Language.Haskell.TH ( mkBytes, bytesPrimL )
+import qualified Data.ByteString.Internal as B
+#endif
 import System.Directory (doesDirectoryExist, doesFileExist,
                          getDirectoryContents, canonicalizePath)
-import Control.Exception (throw, ErrorCall(..))
-import Control.Monad (filterM)
+import Control.Exception (throw, tryJust, ErrorCall(..))
+import Control.Monad ((<=<), filterM, guard)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
 import Control.Arrow ((&&&), second)
 import Control.Applicative ((<$>))
 import Data.ByteString.Unsafe (unsafePackAddressLen)
+import System.IO.Error (isDoesNotExistError)
 import System.IO.Unsafe (unsafePerformIO)
 import System.FilePath ((</>), takeDirectory, takeExtension)
 import Data.String (fromString)
 import Prelude as P
+import Data.List (sortBy)
+import Data.Ord (comparing)
 
 -- | Embed a single file in your source code.
 --
@@ -84,13 +91,46 @@
 #endif
   (runIO $ B.readFile fp) >>= bsToExp
 
+-- | Embed a single file in your source code.
+--   Unlike 'embedFile', path is given relative to project root.
+-- @since 0.0.16.0
+embedFileRelative :: FilePath -> Q Exp
+embedFileRelative = embedFile <=< makeRelativeToProject
+
+-- | Maybe embed a single file in your source code depending on whether or not file exists.
+--
+-- Warning: When a build is compiled with the file missing, a recompile when the file exists might not trigger an embed of the file.
+-- You might try to fix this by doing a clean build.
+--
+-- > import qualified Data.ByteString
+-- >
+-- > maybeMyFile :: Maybe Data.ByteString.ByteString
+-- > maybeMyFile = $(embedFileIfExists "dirName/fileName")
+--
+-- @since 0.0.14.0
+embedFileIfExists :: FilePath -> Q Exp
+embedFileIfExists fp = do
+  mbs <- runIO maybeFile
+  case mbs of
+    Nothing -> [| Nothing |]
+    Just bs -> do
+#if MIN_VERSION_template_haskell(2,7,0)
+      qAddDependentFile fp
+#endif
+      [| Just $(bsToExp bs) |]
+  where
+    maybeFile :: IO (Maybe B.ByteString)
+    maybeFile = 
+      either (const Nothing) Just <$> 
+      tryJust (guard . isDoesNotExistError) (B.readFile fp)
+
 -- | Embed a single existing file in your source code
 -- out of list a list of paths supplied.
 --
 -- > import qualified Data.ByteString
 -- >
 -- > myFile :: Data.ByteString.ByteString
--- > myFile = $(embedFile' [ "dirName/fileName", "src/dirName/fileName" ])
+-- > myFile = $(embedOneFileOf [ "dirName/fileName", "src/dirName/fileName" ])
 embedOneFileOf :: [FilePath] -> Q Exp
 embedOneFileOf ps =
   (runIO $ readExistingFile ps) >>= \ ( path, content ) -> do
@@ -118,6 +158,18 @@
     e <- ListE <$> ((runIO $ fileList fp) >>= mapM (pairToExp fp))
     return $ SigE e typ
 
+-- | Embed a directory listing recursively in your source code.
+--
+-- > myFiles :: [FilePath]
+-- > myFiles = $(embedDirListing "dirName")
+--
+-- @since 0.0.11
+embedDirListing :: FilePath -> Q Exp
+embedDirListing fp = do
+    typ <- [t| [FilePath] |]
+    e <- ListE <$> ((runIO $ fmap fst <$> fileList fp) >>= mapM strToExp)
+    return $ SigE e typ
+
 -- | Get a directory tree in the IO monad.
 --
 -- This is the workhorse of 'embedDir'
@@ -130,7 +182,11 @@
     qAddDependentFile $ _root ++ '/' : path
 #endif
     exp' <- bsToExp bs
-    return $! TupE [LitE $ StringL path, exp']
+    return $! TupE
+#if MIN_VERSION_template_haskell(2,16,0)
+      $ map Just
+#endif
+      [LitE $ StringL path, exp']
 
 bsToExp :: B.ByteString -> Q Exp
 #if MIN_VERSION_template_haskell(2, 5, 0)
@@ -138,7 +194,11 @@
     return $ VarE 'unsafePerformIO
       `AppE` (VarE 'unsafePackAddressLen
       `AppE` LitE (IntegerL $ fromIntegral $ B8.length bs)
-#if MIN_VERSION_template_haskell(2, 8, 0)
+#if MIN_VERSION_template_haskell(2, 16, 0)
+      `AppE` LitE (bytesPrimL (
+                let B.PS ptr off sz = bs
+                in  mkBytes ptr (fromIntegral off) (fromIntegral sz))))
+#elif MIN_VERSION_template_haskell(2, 8, 0)
       `AppE` LitE (StringPrimL $ B.unpack bs))
 #else
       `AppE` LitE (StringPrimL $ B8.unpack bs))
@@ -213,7 +273,7 @@
              mapM (liftPair2 . second B.readFile)
     dirs <- filterM (doesDirectoryExist . snd) all' >>=
             mapM (fileList' realTop . fst)
-    return $ concat $ files : dirs
+    return $ sortBy (comparing fst) $ concat $ files : dirs
 
 liftPair2 :: Monad m => (a, m b) -> m (a, b)
 liftPair2 (a, b) = b >>= \b' -> return (a, b')
@@ -352,13 +412,23 @@
 -- finds the first parent directory with a .cabal file, and uses that as the
 -- root directory for fixing the relative path.
 --
--- @@@
--- $(makeRelativeToProject "data/foo.txt" >>= embedFile)
--- @@@
+-- @$(makeRelativeToProject "data/foo.txt" >>= embedFile)@
 --
 -- @since 0.0.10
 makeRelativeToProject :: FilePath -> Q FilePath
-makeRelativeToProject rel = do
+makeRelativeToProject = makeRelativeToLocationPredicate $ (==) ".cabal" . takeExtension
+
+-- | Take a predicate to infer the project root and a relative file path, the given file path is then attached to the inferred project root
+--
+-- This function looks at the source location of the Haskell file calling it,
+-- finds the first parent directory with a file matching the given predicate, and uses that as the
+-- root directory for fixing the relative path.
+--
+-- @$(makeRelativeToLocationPredicate ((==) ".cabal" . takeExtension) "data/foo.txt" >>= embedFile)@
+--
+-- @since 0.0.15.0
+makeRelativeToLocationPredicate :: (FilePath -> Bool) -> FilePath -> Q FilePath
+makeRelativeToLocationPredicate isTargetFile rel = do
     loc <- qLocation
     runIO $ do
         srcFP <- canonicalizePath $ loc_filename loc
@@ -373,8 +443,6 @@
             then return Nothing
             else do
                 contents <- getDirectoryContents dir
-                if any isCabalFile contents
+                if any isTargetFile contents
                     then return (Just dir)
                     else findProjectDir dir
-
-    isCabalFile fp = takeExtension fp == ".cabal"
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
 ## file-embed
 
+![Tests](https://github.com/snoyberg/file-embed/workflows/Tests/badge.svg)
+
 Use Template Haskell to read a file or all the files in a directory, and turn
 them into (path, bytestring) pairs embedded in your haskell code.
diff --git a/file-embed.cabal b/file-embed.cabal
--- a/file-embed.cabal
+++ b/file-embed.cabal
@@ -1,6 +1,6 @@
 name:            file-embed
-version:         0.0.10.1
-license:         BSD3
+version:         0.0.16.0
+license:         BSD2
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
 maintainer:      Michael Snoyman <michael@snoyman.com>
@@ -10,7 +10,7 @@
                  embedded in your Haskell code.
 category:        Data
 stability:       Stable
-cabal-version:   >= 1.8
+cabal-version:   >= 1.10
 build-type:      Simple
 homepage:        https://github.com/snoyberg/file-embed
 extra-source-files: test/main.hs, test/sample/foo, test/sample/bar/baz,
@@ -18,7 +18,8 @@
                     README.md
 
 library
-    build-depends:   base               >= 4       && < 5
+    default-language: Haskell2010
+    build-depends:   base               >= 4.9.1   && < 5
                    , bytestring         >= 0.9.1.4
                    , directory          >= 1.0.0.3
                    , template-haskell
@@ -27,10 +28,12 @@
     ghc-options:     -Wall
 
 test-suite test
+    default-language: Haskell2010
     type: exitcode-stdio-1.0
     main-is: main.hs
     hs-source-dirs: test
     build-depends: base
+                 , bytestring
                  , file-embed
                  , filepath
 
diff --git a/test/main.hs b/test/main.hs
--- a/test/main.hs
+++ b/test/main.hs
@@ -2,6 +2,7 @@
 {-# LANGUAGE OverloadedStrings #-}
 
 import Control.Monad (unless)
+import qualified Data.ByteString as B (ByteString, filter)
 import Data.FileEmbed
 import System.FilePath ((</>))
 
@@ -14,8 +15,14 @@
 main = do
     let received = $(embedDir "test/sample")
     received @?=
-        [ ("foo", "foo\r\n")
-        , ("bar" </> "baz", "baz\r\n")
+        [ ("bar" </> "baz", "baz\r\n")
+        , ("foo", "foo\r\n")
         ]
     let str = $(embedStringFile "test/sample/foo") :: String
-    str @?= "foo\r\n"
+    filter (/= '\r') str @?= "foo\n"
+
+    let mbs = $(embedFileIfExists "test/sample/foo")
+    fmap (B.filter (/= fromIntegral (fromEnum '\r'))) mbs @?= Just "foo\n"
+
+    let mbs2 = $(embedFileIfExists "test/sample/foo2") :: Maybe B.ByteString
+    mbs2 @?= Nothing
