diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for circe
 
+## 1.1.0.0 -- 8-16-26
+
+* Vector table generator
+
 ## 1.0.0.0 -- 8-16-26
 
 * Split internal and external API
diff --git a/circe.cabal b/circe.cabal
--- a/circe.cabal
+++ b/circe.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               circe
-version:            1.0.0.0
+version:            1.1.0.0
 synopsis:           CRC
 description:        Cyclic redundancy check CRC8 CRC16 CRC32 CRC64
 homepage:           https://github.com/dopamane/circe
@@ -42,6 +42,7 @@
     bytestring,
     circe,
     optparse-applicative,
+    vector
 
 test-suite test
   default-language: Haskell2010
diff --git a/exe/Main.hs b/exe/Main.hs
--- a/exe/Main.hs
+++ b/exe/Main.hs
@@ -4,6 +4,7 @@
 import Codec.Circe.Internal
 import Data.ByteString.Lazy (ByteString)
 import qualified Data.ByteString.Lazy as BS
+import qualified Data.Vector.Storable as V
 import Data.Version
 import Data.Word
 import Options.Applicative
@@ -13,14 +14,17 @@
 main = do
   cli <- circeCLI $ showVersion version
   case cli of
-    CRC8Table  p -> putStrLn $ table $ w8  <$> crc8Table  p
-    CRC16Table p -> putStrLn $ table $ w16 <$> crc16Table p
-    CRC32Table p -> putStrLn $ table $ w32 <$> crc32Table p
-    CRC64Table p -> putStrLn $ table $ w64 <$> crc64Table p
+    CRC8Table  p -> display w8  $ crc8Table  p
+    CRC16Table p -> display w16 $ crc16Table p
+    CRC32Table p -> display w32 $ crc32Table p
+    CRC64Table p -> display w64 $ crc64Table p
     CRC8  cfg fM -> stream fM $ w8  . crc8  cfg
     CRC16 cfg fM -> stream fM $ w16 . crc16 cfg
     CRC32 cfg fM -> stream fM $ w32 . crc32 cfg
     CRC64 cfg fM -> stream fM $ w64 . crc64 cfg
+
+display :: V.Storable a => (a -> String) -> V.Vector a -> IO ()
+display f = putStrLn . table . map f . V.toList
 
 stream :: Maybe String -> (ByteString -> String) -> IO ()
 stream Nothing     f = putStrLn . f =<< BS.getContents
diff --git a/lib/Codec/Circe.hs b/lib/Codec/Circe.hs
--- a/lib/Codec/Circe.hs
+++ b/lib/Codec/Circe.hs
@@ -57,16 +57,16 @@
 crc64WithTable = crcWithTable ref64
 
 -- | generate CRC8 lookup table using polynomial
-crc8Table :: Word8 -> [Word8]
+crc8Table :: Word8 -> Vector Word8
 crc8Table = crcTable
 -- | generate CRC16 lookup table using polynomial
-crc16Table :: Word16 -> [Word16]
+crc16Table :: Word16 -> Vector Word16
 crc16Table = crcTable
 -- | generate CRC32 lookup table using polynomial
-crc32Table :: Word32 -> [Word32]
+crc32Table :: Word32 -> Vector Word32
 crc32Table = crcTable
 -- | generate CRC64 lookup table using polynomial
-crc64Table :: Word64 -> [Word64]
+crc64Table :: Word64 -> Vector Word64
 crc64Table = crcTable
 
 -- | specialized CRC for 'Word8'
diff --git a/lib/Codec/Circe/Internal.hs b/lib/Codec/Circe/Internal.hs
--- a/lib/Codec/Circe/Internal.hs
+++ b/lib/Codec/Circe/Internal.hs
@@ -52,7 +52,7 @@
 crcInternal :: (FiniteBits a, Integral a, Num a, V.Storable a) => (a -> a) -> CRCCfg a -> ByteString -> a
 crcInternal ref cfg = crcWithTable ref t cfg
   where
-    t = V.fromList $ crcTable $ crcPoly cfg
+    t = crcTable $ crcPoly cfg
 
 -- | calculate CRC with reflection & precalculated poly table.
 crcWithTable
@@ -76,13 +76,12 @@
     w' = finiteBitSize crc - 8
 
 -- | Generate CRC table using width and poly
-crcTable :: (FiniteBits a, Enum a, Num a) => a -> [a]
-crcTable poly = calc <$> [0..255]
+crcTable :: (FiniteBits a, Num a, V.Storable a) => a -> Vector a
+crcTable poly = V.generate 256 $ \idx ->
+  V.unsafeLast $ V.iterateN 9 step $ fromIntegral idx `shiftL` (l - 8)
   where
-    l    = fromIntegral (finiteBitSize poly) :: Int
-    calc = (!! 8) . iterate step . (`shiftL` (l - 8))
-      where
-        step curByte = applyWhen (testBit curByte $ l - 1) (xor poly) $ curByte `shiftL` 1
+    l = fromIntegral (finiteBitSize poly) :: Int
+    step curByte = applyWhen (testBit curByte $ l - 1) (xor poly) $ curByte `shiftL` 1
 
 -- | @0x0a@
 w8 :: Word8 -> String
