diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## 0.2.0
+
+- Add xstaticMiddleware.
+- Enable StrictData.
+- Remove file-embed dependency, use xstatic-th instead.
+- Simplify request file path and etag data type.
+
 ## 0.1.0
 
 - Initial release
diff --git a/src/XStatic.hs b/src/XStatic.hs
--- a/src/XStatic.hs
+++ b/src/XStatic.hs
@@ -1,73 +1,89 @@
 {-# LANGUAGE ImportQualifiedPost #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE StrictData #-}
 
 module XStatic (
     -- * xstatic api
     XStaticFile (..),
     xstaticApp,
+    xstaticMiddleware,
 
-    -- * file-embed re-export
-    embedFile,
+    -- * helpers
+    isGzip,
 ) where
 
 import Data.Binary.Builder (Builder, fromByteString)
 import Data.ByteString (ByteString)
 import Data.ByteString qualified as BS
-import Data.ByteString.Char8 (breakEnd, pack)
-import Data.ByteString.Char8 qualified as BS8
-import Data.FileEmbed (embedFile)
+import Data.ByteString.Char8 (pack)
+import Data.ByteString.Unsafe qualified as BS (unsafeDrop)
 import Data.Map.Strict qualified as Map
-import Data.Version (Version, showVersion)
 import Network.HTTP.Types.Header (ResponseHeaders)
 import Network.HTTP.Types.Status qualified as HTTP
 import Network.Wai qualified
 
--- | A static file definition
+{- | A static file definition.
+
+ Use the @xstatic-th@ or @file-embed@ library to embed a local file.
+-}
 data XStaticFile = XStaticFile
-    { name :: ByteString
-    -- ^ The expected request filename. The name must not have any slash '/'.
-    , content :: ByteString
-    -- ^ The file content gzip compressed.
-    , contentVersion :: Version
-    -- ^ The file version for the etag header.
-    , contentType :: ByteString
-    -- ^ The content type, e.g. `text/javascript` or `text/css`.
+    { xfPath :: ByteString
+    -- ^ The expected request path.
+    , xfContent :: ByteString
+    -- ^ The file content.
+    , xfETag :: ByteString
+    -- ^ The etag header value.
+    , xfType :: ByteString
+    -- ^ The content type, e.g. \"application\/javascript\" or \"text\/css\".
     }
 
 {- | Create a wai application to serve 'XStaticFile'.
 
- The request are served whenever the basename match, ignoring the parent directory or the query string.
+ The \"\/xstatic\/\" request path prefix is ignored.
 -}
 xstaticApp :: [XStaticFile] -> Network.Wai.Application
-xstaticApp xs = \req resp ->
-    let (_, basename) = breakEnd (== '/') (Network.Wai.rawPathInfo req)
-     in resp $ case Map.lookup basename files of
-            Just (builder, headers) ->
-                let body = case Network.Wai.requestMethod req of
-                        "HEAD" -> mempty
-                        _ -> builder
-                 in Network.Wai.responseBuilder HTTP.status200 headers body
-            Nothing -> Network.Wai.responseLBS HTTP.status404 mempty mempty
+xstaticApp xs = \req resp -> do
+    let basePath = Network.Wai.rawPathInfo req
+        requestPath
+            | "/xstatic/" `BS.isPrefixOf` basePath = BS.unsafeDrop 8 basePath
+            | otherwise = basePath
+
+    resp $ case Map.lookup requestPath files of
+        Just (builder, headers) ->
+            let body = case Network.Wai.requestMethod req of
+                    "HEAD" -> mempty
+                    _ -> builder
+             in Network.Wai.responseBuilder HTTP.status200 headers body
+        Nothing -> Network.Wai.responseLBS HTTP.status404 mempty mempty
   where
     files :: Map.Map ByteString (Builder, ResponseHeaders)
     files = Map.fromList $ map toItem xs
 
     toItem :: XStaticFile -> (ByteString, (Builder, ResponseHeaders))
     toItem xf =
-        ( name xf
+        ( xfPath xf
         ,
-            ( fromByteString $ content xf
-            , (addGzipHeader $ content xf)
+            ( fromByteString $ xfContent xf
+            , (addGzipHeader $ xfContent xf)
                 [ ("cache-control", "public, max-age=604800")
-                , ("content-length", pack . show . BS.length $ content xf)
-                , ("content-type", contentType xf)
+                , ("content-length", pack . show . BS.length $ xfContent xf)
+                , ("content-type", xfType xf)
                 , ("connection", "keep-alive")
-                , ("etag", versionToEtag $ contentVersion xf)
+                , ("etag", xfETag xf)
                 , ("keep-alive", "timeout=5, max=100")
                 ]
             )
         )
 
+-- | Serve 'XStaticFile' using 'xstaticApp' when the provided application returns a 404.
+xstaticMiddleware :: [XStaticFile] -> Network.Wai.Middleware
+xstaticMiddleware xs app req resp = app req handleAppResp
+  where
+    staticApp = xstaticApp xs
+    handleAppResp appResp = case HTTP.statusCode (Network.Wai.responseStatus appResp) of
+        404 -> staticApp req resp
+        _ -> resp appResp
+
 addGzipHeader :: ByteString -> ResponseHeaders -> ResponseHeaders
 addGzipHeader fileContent
     | isGzip fileContent = (("content-encoding", "gzip") :)
@@ -75,6 +91,3 @@
 
 isGzip :: ByteString -> Bool
 isGzip = BS.isPrefixOf "\x1f\x8b\x08" -- the gzip magic number for deflate
-
-versionToEtag :: Version -> ByteString
-versionToEtag = BS8.unwords . BS8.split '.' . pack . showVersion
diff --git a/xstatic.cabal b/xstatic.cabal
--- a/xstatic.cabal
+++ b/xstatic.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           xstatic
-version:        0.1.0
+version:        0.2.0
 synopsis:       Low-Fat static file packaging for Haskell projects
 description:    Low-Fat static file packaging for Haskell projects.
 category:       JavaScript
@@ -36,7 +36,6 @@
     , binary
     , bytestring
     , containers
-    , file-embed
     , http-types
     , wai
   default-language: Haskell2010
