diff --git a/Yesod/Static.hs b/Yesod/Static.hs
--- a/Yesod/Static.hs
+++ b/Yesod/Static.hs
@@ -29,6 +29,7 @@
     , embed
       -- * Template Haskell helpers
     , staticFiles
+    , staticFilesList
     , publicFiles
       -- * Hashing
     , base64md5
@@ -155,6 +156,25 @@
 staticFiles :: Prelude.FilePath -> Q [Dec]
 staticFiles dir = mkStaticFiles dir
 
+-- | Same as 'staticFiles', but takes an explicit list of files to create
+-- identifiers for. The files are given relative to the static folder. For
+-- example, to get the files \"static/js/jquery.js\" and
+-- \"static/css/normalize.css\", you would use:
+--
+-- > staticFilesList "static" ["js/jquery.js"], ["css/normalize.css"]]
+--
+-- This can be useful when you have a very large number of static files, but
+-- only need to refer to a few of them from Haskell.
+staticFilesList :: Prelude.FilePath -> [Prelude.FilePath] -> Q [Dec]
+staticFilesList dir fs =
+    mkStaticFilesList dir (map split fs) "StaticRoute" True
+  where
+    split :: Prelude.FilePath -> [String]
+    split [] = []
+    split x =
+        let (a, b) = break (== '/') x
+         in a : split (drop 1 b)
+
 -- | like staticFiles, but doesn't append an etag to the query string
 -- This will compile faster, but doesn't achieve as great of caching.
 -- The browser can avoid downloading the file, but it always needs to send a request with the etag value or the last-modified value to the server to see if its copy is up to dat
@@ -212,6 +232,15 @@
                -> Q [Dec]
 mkStaticFiles' fp routeConName makeHash = do
     fs <- qRunIO $ getFileListPieces fp
+    mkStaticFilesList fp fs routeConName makeHash
+
+mkStaticFilesList
+    :: Prelude.FilePath -- ^ static directory
+    -> [[String]] -- ^ list of files to create identifiers for
+    -> String   -- ^ route constructor "StaticRoute"
+    -> Bool     -- ^ append checksum query parameter
+    -> Q [Dec]
+mkStaticFilesList fp fs routeConName makeHash = do
     concat `fmap` mapM mkRoute fs
   where
     replace' c
@@ -233,7 +262,6 @@
         pack' <- [|pack|]
         qs <- if makeHash
                     then do hash <- qRunIO $ base64md5File $ pathFromRawPieces fp f
-        -- FIXME hash <- qRunIO . calcHash $ fp ++ '/' : intercalate "/" f
                             [|[(pack $(lift hash), mempty)]|]
                     else return $ ListE []
         return
diff --git a/tests/runtests.hs b/tests/runtests.hs
new file mode 100644
--- /dev/null
+++ b/tests/runtests.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE OverloadedStrings #-}
+import Yesod.Static ()
+
+import Test.Hspec
+import Test.Hspec.HUnit ()
+-- import Test.Hspec.QuickCheck (prop)
+
+main :: IO ()
+main = return () -- hspecX $ return []
+{- FIXME specs
+
+specs :: IO [Spec]
+specs = runSpecM $ do
+  context "get file list" $ do
+    ti "pieces" $ do
+      x <- getFileListPieces "tests/data"
+      x @?= [["foo"], ["bar", "baz"]]-}
diff --git a/yesod-static.cabal b/yesod-static.cabal
--- a/yesod-static.cabal
+++ b/yesod-static.cabal
@@ -1,5 +1,5 @@
 name:            yesod-static
-version:         0.3.0.1
+version:         0.3.1
 license:         BSD3
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -11,6 +11,8 @@
 build-type:      Simple
 homepage:        http://www.yesodweb.com/
 description:     Static file serving subsite for Yesod Web Framework.
+extra-source-files:
+  tests/runtests.hs
 
 flag test
   description: Build the executable to run unit tests
@@ -44,7 +46,7 @@
     cpp-options:   -DTEST
     build-depends:   yesod-static
                    , base >= 4 && < 5
-                   , hspec >= 0.6.1 && < 0.7
+                   , hspec >= 0.8   && < 0.9
                    , HUnit
     ghc-options:     -Wall
     main-is:         runtests.hs
