diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.1.3.0
+
+* Add modules for `Storable` and boxed vectors.
+
 # 0.1.2.0
 
 * Add liquid haskell refinement for `new`.
diff --git a/grow-vector.cabal b/grow-vector.cabal
--- a/grow-vector.cabal
+++ b/grow-vector.cabal
@@ -1,7 +1,7 @@
 name:                grow-vector
 synopsis:            Mutable vector with efficient appends
 description:         Mutable vector with efficient updates. Simple implementation on partially filled array with capacity tracking and resizing.
-version:             0.1.2.0
+version:             0.1.3.0
 license:             MIT
 license-file:        LICENSE
 copyright:           2020 Anton Gushcha
@@ -26,6 +26,8 @@
 library
   hs-source-dirs:      src
   exposed-modules:
+    Data.Vector.Grow
+    Data.Vector.Grow.Storable
     Data.Vector.Grow.Unboxed
 
   build-depends:
@@ -50,7 +52,9 @@
   main-is:             Driver.hs
   hs-source-dirs:      test
   other-modules:
-      Data.Vector.Grow.Unboxed.Test
+    Data.Vector.Grow.Storable.Test
+    Data.Vector.Grow.Test
+    Data.Vector.Grow.Unboxed.Test
   ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       base
diff --git a/src/Data/Vector/Grow.hs b/src/Data/Vector/Grow.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Vector/Grow.hs
@@ -0,0 +1,230 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies #-}
+-- |
+-- Module      : Data.Vector.Grow
+-- Copyright   : (c) 2020 Gushcha Anton
+-- License     : MIT
+-- Maintainer  : ncrashed@protonmail.com
+-- Stability   : unstable
+-- Portability : non-portable
+--
+-- Module defines mutable vector that can grow in size automatically when an user
+-- adds new elements at the end of vector.
+--
+-- We reallocate vector with 1.5x length to get amortized append.
+module Data.Vector.Grow(
+    GrowVector(..)
+  , IOGrowVector
+  -- * Quering info about vector
+  , length
+  , null
+  , capacity
+  -- * Creation
+  , new
+  , newSized
+  -- * Quering subvectors
+  , slice
+  -- * Converting to immutable
+  , thaw
+  , freeze
+  -- * Capacity maninuplation
+  , ensure
+  , ensureAppend
+  -- * Accessing individual elements
+  , read
+  , write
+  , unsafeRead
+  , unsafeWrite
+  -- * Appending to vector
+  , pushBack
+  , unsafePushBack
+  ) where
+
+import Control.Monad
+import Control.Monad.Primitive
+import Data.Primitive.MutVar
+import Data.Vector.Mutable (MVector)
+import GHC.Generics
+import Prelude hiding (length, null, read)
+
+import qualified Data.Vector as U
+import qualified Data.Vector.Mutable as M
+
+-- | Grow vector that is wrap around mutable vector. We allocate partially filled
+-- vector and grow it when there is no more space for new element.
+data GrowVector s a = GrowVector {
+  growVector         :: !(MutVar s (MVector s a))
+, growVectorLength   :: !(MutVar s Int)
+} deriving (Generic)
+
+-- | Synonim for 'GrowVector' in 'IO' monad
+type IOGrowVector a = GrowVector RealWorld a
+
+-- | Return current capacity of the vector (amount of elements that it can fit without realloc)
+capacity :: (PrimMonad m) => GrowVector (PrimState m) a -> m Int
+capacity v = do
+  mv <- readMutVar $ growVector v
+  pure $ M.length mv
+{-# INLINE capacity #-}
+
+-- | Return current amount of elements in the vector
+length :: (PrimMonad m) => GrowVector (PrimState m) a -> m Int
+length v = readMutVar $ growVectorLength v
+{-# INLINE length #-}
+
+-- | Return 'True' if there is no elements inside the vector
+null :: (PrimMonad m) => GrowVector (PrimState m) a -> m Bool
+null v = fmap (== 0) $ length v
+{-# INLINE null #-}
+
+-- | Allocation of new growable vector with given capacity.
+new :: (PrimMonad m) => Int -> m (GrowVector (PrimState m) a)
+new = newSized 0
+{-# INLINE new #-}
+
+-- | Allocation of new growable vector with given filled size and capacity.
+-- Elements is not initialized. Capacity must be greater than filled size.
+newSized :: (PrimMonad m) => Int -> Int -> m (GrowVector (PrimState m) a)
+newSized n cap = GrowVector <$> (newMutVar =<< M.new cap) <*> newMutVar n
+{-# INLINABLE newSized #-}
+
+-- | Yield a part of mutable vector without copying it. The vector must contain at least i+n elements.
+slice :: (PrimMonad m)
+  => Int -- ^ i starting index
+  -> Int -- ^ n number of elements
+  -> GrowVector (PrimState m) a
+  -> m (GrowVector (PrimState m) a)
+slice i n v = do
+  newSize <- newMutVar n
+  mv <- readMutVar $ growVector v
+  newVec <- newMutVar $! M.slice i n mv
+  pure $! GrowVector newVec newSize
+{-# INLINABLE slice #-}
+
+-- | Convert immutable vector to grow mutable version. Doesn't allocate additonal memory for appending,
+-- use 'ensure' to add capacity to the vector.
+thaw :: (PrimMonad m)
+  => U.Vector a
+  -> m (GrowVector (PrimState m) a)
+thaw u = do
+  mv <- newMutVar =<< U.thaw u
+  lv <- newMutVar (U.length u)
+  pure $ GrowVector mv lv
+{-# INLINABLE thaw #-}
+
+-- | Freezing growable vector. It will contain only actual elements of the vector not including capacity
+-- space, but you should call 'U.force' on resulting vector to not hold the allocated capacity of original
+-- vector in memory.
+
+freeze :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> m (U.Vector a)
+freeze v = do
+  n <- length v
+  mv <- readMutVar $ growVector v
+  U.freeze $ M.take n mv
+{-# INLINABLE freeze #-}
+
+-- | Ensure that grow vector has at least given capacity possibly with reallocation.
+ensure :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int
+  -> m ()
+ensure v n = do
+  c <- capacity v
+  unless (c >= n) $ do
+    mv <- readMutVar $ growVector v
+    writeMutVar (growVector v) =<< M.grow mv (n - c)
+{-# INLINABLE ensure #-}
+
+-- | Ensure that grow vector has enough space for additonal n elements.
+-- We grow vector by 1.5 factor or by required elements count * 1.5.
+ensureAppend :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Additional n elements
+  -> m ()
+ensureAppend v i = do
+  n <- readMutVar $ growVectorLength v
+  mv <- readMutVar $ growVector v
+  let c = M.length mv
+  unless (c >= n + i) $ do
+    let growFactor = 1.5
+        newCap = ceiling $ max (growFactor * fromIntegral c) (fromIntegral c + growFactor * fromIntegral (n + i - c))
+    writeMutVar (growVector v) =<< M.grow mv (newCap - c)
+{-# INLINABLE ensureAppend #-}
+
+-- | Read element from vector at given index.
+read :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> m a
+read v i = do
+  n <- readMutVar $ growVectorLength v
+#ifndef LIQUID
+  when (i < 0 || i >= n) $ error $ "GrowVector.read: index " <> show i <> " is out bounds " <> show n
+#endif
+  mv <- readMutVar $ growVector v
+  M.unsafeRead mv i
+{-# INLINABLE read #-}
+
+-- | Read element from vector at given index.
+unsafeRead :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> m a
+unsafeRead v i = do
+  mv <- readMutVar $ growVector v
+  M.unsafeRead mv i
+{-# INLINABLE unsafeRead #-}
+
+-- | Write down element in the vector at given index.
+write :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> a
+  -> m ()
+write v i a = do
+  n <- readMutVar $ growVectorLength v
+#ifndef LIQUID
+  when (i < 0 || i >= n) $ error $ "GrowVector.write: index " <> show i <> " is out bounds " <> show n
+#endif
+  mv <- readMutVar $ growVector v
+  M.unsafeWrite mv i a
+{-# INLINABLE write #-}
+
+-- | Write down element in the vector at given index.
+unsafeWrite :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> a
+  -> m ()
+unsafeWrite v i a = do
+  mv <- readMutVar $ growVector v
+  M.unsafeWrite mv i a
+{-# INLINABLE unsafeWrite #-}
+
+-- | O(1) amortized appending to vector
+pushBack :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> a
+  -> m ()
+pushBack v a = do
+  ensureAppend v 1
+  unsafePushBack v a
+{-# INLINABLE pushBack #-}
+
+-- | O(1) amortized appending to vector. Doesn't reallocate vector, so
+-- there must by capacity - length >= 1.
+unsafePushBack :: (PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> a
+  -> m ()
+unsafePushBack v a = do
+  n <- readMutVar $ growVectorLength v
+  mv <- readMutVar $ growVector v
+  M.write mv n a
+  writeMutVar (growVectorLength v) (n+1)
+{-# INLINABLE unsafePushBack #-}
diff --git a/src/Data/Vector/Grow/Storable.hs b/src/Data/Vector/Grow/Storable.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Vector/Grow/Storable.hs
@@ -0,0 +1,230 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies #-}
+-- |
+-- Module      : Data.Vector.Grow.Storable
+-- Copyright   : (c) 2020 Gushcha Anton
+-- License     : MIT
+-- Maintainer  : ncrashed@protonmail.com
+-- Stability   : unstable
+-- Portability : non-portable
+--
+-- Module defines mutable vector that can grow in size automatically when an user
+-- adds new elements at the end of vector.
+--
+-- We reallocate vector with 1.5x length to get amortized append.
+module Data.Vector.Grow.Storable(
+    GrowVector(..)
+  , IOGrowVector
+  -- * Quering info about vector
+  , length
+  , null
+  , capacity
+  -- * Creation
+  , new
+  , newSized
+  -- * Quering subvectors
+  , slice
+  -- * Converting to immutable
+  , thaw
+  , freeze
+  -- * Capacity maninuplation
+  , ensure
+  , ensureAppend
+  -- * Accessing individual elements
+  , read
+  , write
+  , unsafeRead
+  , unsafeWrite
+  -- * Appending to vector
+  , pushBack
+  , unsafePushBack
+  ) where
+
+import Control.Monad
+import Control.Monad.Primitive
+import Data.Primitive.MutVar
+import Data.Vector.Storable.Mutable (MVector, Storable)
+import GHC.Generics
+import Prelude hiding (length, null, read)
+
+import qualified Data.Vector.Storable as V
+import qualified Data.Vector.Storable.Mutable as M
+
+-- | Grow vector that is wrap around mutable vector. We allocate partially filled
+-- vector and grow it when there is no more space for new element.
+data GrowVector s a = GrowVector {
+  growVector         :: !(MutVar s (MVector s a))
+, growVectorLength   :: !(MutVar s Int)
+} deriving (Generic)
+
+-- | Synonim for 'GrowVector' in 'IO' monad
+type IOGrowVector a = GrowVector RealWorld a
+
+-- | Return current capacity of the vector (amount of elements that it can fit without realloc)
+capacity :: (Storable a, PrimMonad m) => GrowVector (PrimState m) a -> m Int
+capacity v = do
+  mv <- readMutVar $ growVector v
+  pure $ M.length mv
+{-# INLINE capacity #-}
+
+-- | Return current amount of elements in the vector
+length :: (Storable a, PrimMonad m) => GrowVector (PrimState m) a -> m Int
+length v = readMutVar $ growVectorLength v
+{-# INLINE length #-}
+
+-- | Return 'True' if there is no elements inside the vector
+null :: (Storable a, PrimMonad m) => GrowVector (PrimState m) a -> m Bool
+null v = fmap (== 0) $ length v
+{-# INLINE null #-}
+
+-- | Allocation of new growable vector with given capacity.
+new :: (Storable a, PrimMonad m) => Int -> m (GrowVector (PrimState m) a)
+new = newSized 0
+{-# INLINE new #-}
+
+-- | Allocation of new growable vector with given filled size and capacity.
+-- Elements is not initialized. Capacity must be greater than filled size.
+newSized :: (Storable a, PrimMonad m) => Int -> Int -> m (GrowVector (PrimState m) a)
+newSized n cap = GrowVector <$> (newMutVar =<< M.new cap) <*> newMutVar n
+{-# INLINABLE newSized #-}
+
+-- | Yield a part of mutable vector without copying it. The vector must contain at least i+n elements.
+slice :: (Storable a, PrimMonad m)
+  => Int -- ^ i starting index
+  -> Int -- ^ n number of elements
+  -> GrowVector (PrimState m) a
+  -> m (GrowVector (PrimState m) a)
+slice i n v = do
+  newSize <- newMutVar n
+  mv <- readMutVar $ growVector v
+  newVec <- newMutVar $! M.slice i n mv
+  pure $! GrowVector newVec newSize
+{-# INLINABLE slice #-}
+
+-- | Convert immutable vector to grow mutable version. Doesn't allocate additonal memory for appending,
+-- use 'ensure' to add capacity to the vector.
+thaw :: (Storable a, PrimMonad m)
+  => V.Vector a
+  -> m (GrowVector (PrimState m) a)
+thaw u = do
+  mv <- newMutVar =<< V.thaw u
+  lv <- newMutVar (V.length u)
+  pure $ GrowVector mv lv
+{-# INLINABLE thaw #-}
+
+-- | Freezing growable vector. It will contain only actual elements of the vector not including capacity
+-- space, but you should call 'V.force' on resulting vector to not hold the allocated capacity of original
+-- vector in memory.
+
+freeze :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> m (V.Vector a)
+freeze v = do
+  n <- length v
+  mv <- readMutVar $ growVector v
+  V.freeze $ M.take n mv
+{-# INLINABLE freeze #-}
+
+-- | Ensure that grow vector has at least given capacity possibly with reallocation.
+ensure :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int
+  -> m ()
+ensure v n = do
+  c <- capacity v
+  unless (c >= n) $ do
+    mv <- readMutVar $ growVector v
+    writeMutVar (growVector v) =<< M.grow mv (n - c)
+{-# INLINABLE ensure #-}
+
+-- | Ensure that grow vector has enough space for additonal n elements.
+-- We grow vector by 1.5 factor or by required elements count * 1.5.
+ensureAppend :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Additional n elements
+  -> m ()
+ensureAppend v i = do
+  n <- readMutVar $ growVectorLength v
+  mv <- readMutVar $ growVector v
+  let c = M.length mv
+  unless (c >= n + i) $ do
+    let growFactor = 1.5
+        newCap = ceiling $ max (growFactor * fromIntegral c) (fromIntegral c + growFactor * fromIntegral (n + i - c))
+    writeMutVar (growVector v) =<< M.grow mv (newCap - c)
+{-# INLINABLE ensureAppend #-}
+
+-- | Read element from vector at given index.
+read :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> m a
+read v i = do
+  n <- readMutVar $ growVectorLength v
+#ifndef LIQUID
+  when (i < 0 || i >= n) $ error $ "GrowVector.read: index " <> show i <> " is out bounds " <> show n
+#endif
+  mv <- readMutVar $ growVector v
+  M.unsafeRead mv i
+{-# INLINABLE read #-}
+
+-- | Read element from vector at given index.
+unsafeRead :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> m a
+unsafeRead v i = do
+  mv <- readMutVar $ growVector v
+  M.unsafeRead mv i
+{-# INLINABLE unsafeRead #-}
+
+-- | Write down element in the vector at given index.
+write :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> a
+  -> m ()
+write v i a = do
+  n <- readMutVar $ growVectorLength v
+#ifndef LIQUID
+  when (i < 0 || i >= n) $ error $ "GrowVector.write: index " <> show i <> " is out bounds " <> show n
+#endif
+  mv <- readMutVar $ growVector v
+  M.unsafeWrite mv i a
+{-# INLINABLE write #-}
+
+-- | Write down element in the vector at given index.
+unsafeWrite :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> Int -- ^ Index of element. Must be in [0 .. length) range
+  -> a
+  -> m ()
+unsafeWrite v i a = do
+  mv <- readMutVar $ growVector v
+  M.unsafeWrite mv i a
+{-# INLINABLE unsafeWrite #-}
+
+-- | O(1) amortized appending to vector
+pushBack :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> a
+  -> m ()
+pushBack v a = do
+  ensureAppend v 1
+  unsafePushBack v a
+{-# INLINABLE pushBack #-}
+
+-- | O(1) amortized appending to vector. Doesn't reallocate vector, so
+-- there must by capacity - length >= 1.
+unsafePushBack :: (Storable a, PrimMonad m)
+  => GrowVector (PrimState m) a
+  -> a
+  -> m ()
+unsafePushBack v a = do
+  n <- readMutVar $ growVectorLength v
+  mv <- readMutVar $ growVector v
+  M.write mv n a
+  writeMutVar (growVectorLength v) (n+1)
+{-# INLINABLE unsafePushBack #-}
diff --git a/test/Data/Vector/Grow/Storable/Test.hs b/test/Data/Vector/Grow/Storable/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Vector/Grow/Storable/Test.hs
@@ -0,0 +1,158 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedLists #-}
+module Data.Vector.Grow.Storable.Test where
+
+import Control.Monad.Primitive (RealWorld)
+import Data.Vector.Grow.Storable (GrowVector)
+import qualified Data.Vector.Grow.Storable as V
+
+import Test.Tasty.Hspec
+
+spec_creation :: Spec
+spec_creation = describe "vector creation" $ do
+  it "initialized with right size" $ do
+    v :: GrowVector RealWorld Int <- V.new 42
+    l <- V.length v
+    l `shouldBe` 0
+    c <- V.capacity v
+    c `shouldBe` 42
+    r <- V.null v
+    r `shouldBe` True
+  it "preallocated initialized with right size" $ do
+    v :: GrowVector RealWorld Int <- V.newSized 23 42
+    l <- V.length v
+    l `shouldBe` 23
+    c <- V.capacity v
+    c `shouldBe` 42
+    r <- V.null v
+    r `shouldBe` False
+
+spec_slicing :: Spec
+spec_slicing = describe "vector slicing" $ do
+  it "simple slice" $ do
+    v :: GrowVector RealWorld Int <- V.thaw [1, 2, 3, 4, 5]
+    v' <- V.slice 1 2 v
+    vf <- V.freeze v'
+    vf `shouldBe` [2, 3]
+  it "empty slice" $ do
+    v :: GrowVector RealWorld Int <- V.thaw [1, 2, 3, 4, 5]
+    v' <- V.slice 1 0 v
+    vf <- V.freeze v'
+    vf `shouldBe` []
+  it "begin slice" $ do
+    v :: GrowVector RealWorld Int <- V.thaw [1, 2, 3, 4, 5]
+    v' <- V.slice 0 1 v
+    vf <- V.freeze v'
+    vf `shouldBe` [1]
+  it "end slice" $ do
+    v :: GrowVector RealWorld Int <- V.thaw [1, 2, 3, 4, 5]
+    v' <- V.slice 4 1 v
+    vf <- V.freeze v'
+    vf `shouldBe` [5]
+  it "mutating parent slice" $ do
+    v :: GrowVector RealWorld Int <- V.thaw [1, 2, 3, 4, 5]
+    v' <- V.slice 1 2 v
+    vf1 <- V.freeze v'
+    vf1 `shouldBe` [2, 3]
+    V.write v 2 4
+    vf2 <- V.freeze v'
+    vf2 `shouldBe` [2, 4]
+
+spec_readWrite :: Spec
+spec_readWrite = describe "basic read write" $ do
+  it "read elements" $ do
+    v :: GrowVector RealWorld Int <- V.thaw [1, 2, 3, 4, 5]
+    a1 <- V.read v 0
+    a1 `shouldBe` 1
+    a2 <- V.read v 1
+    a2 `shouldBe` 2
+    a3 <- V.read v 2
+    a3 `shouldBe` 3
+    a4 <- V.read v 4
+    a4 `shouldBe` 5
+  it "writes elements" $ do
+    v :: GrowVector RealWorld Int <- V.newSized 2 2
+    V.write v 0 1
+    V.write v 1 2
+    v' <- V.freeze v
+    v' `shouldBe` [1, 2]
+  it "write-read indemponent" $ do
+    v :: GrowVector RealWorld Int <- V.newSized 1 1
+    V.write v 0 424242
+    a <- V.read v 0
+    a `shouldBe` 424242
+
+spec_ensure :: Spec
+spec_ensure = describe "capacity realloc" $ do
+  it "ensure reallocates properly" $ do
+    v :: GrowVector RealWorld Int <- V.new 5
+    V.ensure v 1
+    c1 <- V.capacity v
+    c1 `shouldBe` 5
+    V.ensure v 6
+    c2 <- V.capacity v
+    c2 `shouldBe` 6
+  it "ensureAppend reallocates properly" $ do
+    v :: GrowVector RealWorld Int <- V.new 5
+    V.ensureAppend v 1
+    c1 <- V.capacity v
+    c1 `shouldBe` 5
+    V.ensureAppend v 6
+    c2 <- V.capacity v
+    c2 `shouldBe` 8
+    V.ensureAppend v 14
+    c3 <- V.capacity v
+    c3 `shouldBe` 17
+
+spec_pushBack :: Spec
+spec_pushBack = describe "push back operations" $ do
+  it "push simple" $ do
+    v :: GrowVector RealWorld Int <- V.new 1
+    V.pushBack v 1
+    l1 <- V.length v
+    c1 <- V.capacity v
+    l1 `shouldBe` 1
+    c1 `shouldBe` 1
+    V.pushBack v 2
+    l2 <- V.length v
+    c2 <- V.capacity v
+    l2 `shouldBe` 2
+    c2 `shouldBe` 3
+    V.pushBack v 3
+    l3 <- V.length v
+    c3 <- V.capacity v
+    l3 `shouldBe` 3
+    c3 `shouldBe` 3
+    V.pushBack v 4
+    l4 <- V.length v
+    c4 <- V.capacity v
+    l4 `shouldBe` 4
+    c4 `shouldBe` 5
+    v' <- V.freeze v
+    v' `shouldBe` [1, 2, 3, 4]
+  it "push unsafe" $ do
+    v :: GrowVector RealWorld Int <- V.new 1
+    V.unsafePushBack v 1
+    l1 <- V.length v
+    c1 <- V.capacity v
+    l1 `shouldBe` 1
+    c1 `shouldBe` 1
+    V.ensureAppend v 1
+    V.unsafePushBack v 2
+    l2 <- V.length v
+    c2 <- V.capacity v
+    l2 `shouldBe` 2
+    c2 `shouldBe` 3
+    V.unsafePushBack v 3
+    l3 <- V.length v
+    c3 <- V.capacity v
+    l3 `shouldBe` 3
+    c3 `shouldBe` 3
+    V.ensureAppend v 1
+    V.unsafePushBack v 4
+    l4 <- V.length v
+    c4 <- V.capacity v
+    l4 `shouldBe` 4
+    c4 `shouldBe` 5
+    v' <- V.freeze v
+    v' `shouldBe` [1, 2, 3, 4]
diff --git a/test/Data/Vector/Grow/Test.hs b/test/Data/Vector/Grow/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Vector/Grow/Test.hs
@@ -0,0 +1,158 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedLists #-}
+module Data.Vector.Grow.Test where
+
+import Control.Monad.Primitive (RealWorld)
+import Data.Vector.Grow (GrowVector)
+import qualified Data.Vector.Grow as G
+
+import Test.Tasty.Hspec
+
+spec_creation :: Spec
+spec_creation = describe "vector creation" $ do
+  it "initialized with right size" $ do
+    v :: GrowVector RealWorld Int <- G.new 42
+    l <- G.length v
+    l `shouldBe` 0
+    c <- G.capacity v
+    c `shouldBe` 42
+    r <- G.null v
+    r `shouldBe` True
+  it "preallocated initialized with right size" $ do
+    v :: GrowVector RealWorld Int <- G.newSized 23 42
+    l <- G.length v
+    l `shouldBe` 23
+    c <- G.capacity v
+    c `shouldBe` 42
+    r <- G.null v
+    r `shouldBe` False
+
+spec_slicing :: Spec
+spec_slicing = describe "vector slicing" $ do
+  it "simple slice" $ do
+    v :: GrowVector RealWorld Int <- G.thaw [1, 2, 3, 4, 5]
+    v' <- G.slice 1 2 v
+    vf <- G.freeze v'
+    vf `shouldBe` [2, 3]
+  it "empty slice" $ do
+    v :: GrowVector RealWorld Int <- G.thaw [1, 2, 3, 4, 5]
+    v' <- G.slice 1 0 v
+    vf <- G.freeze v'
+    vf `shouldBe` []
+  it "begin slice" $ do
+    v :: GrowVector RealWorld Int <- G.thaw [1, 2, 3, 4, 5]
+    v' <- G.slice 0 1 v
+    vf <- G.freeze v'
+    vf `shouldBe` [1]
+  it "end slice" $ do
+    v :: GrowVector RealWorld Int <- G.thaw [1, 2, 3, 4, 5]
+    v' <- G.slice 4 1 v
+    vf <- G.freeze v'
+    vf `shouldBe` [5]
+  it "mutating parent slice" $ do
+    v :: GrowVector RealWorld Int <- G.thaw [1, 2, 3, 4, 5]
+    v' <- G.slice 1 2 v
+    vf1 <- G.freeze v'
+    vf1 `shouldBe` [2, 3]
+    G.write v 2 4
+    vf2 <- G.freeze v'
+    vf2 `shouldBe` [2, 4]
+
+spec_readWrite :: Spec
+spec_readWrite = describe "basic read write" $ do
+  it "read elements" $ do
+    v :: GrowVector RealWorld Int <- G.thaw [1, 2, 3, 4, 5]
+    a1 <- G.read v 0
+    a1 `shouldBe` 1
+    a2 <- G.read v 1
+    a2 `shouldBe` 2
+    a3 <- G.read v 2
+    a3 `shouldBe` 3
+    a4 <- G.read v 4
+    a4 `shouldBe` 5
+  it "writes elements" $ do
+    v :: GrowVector RealWorld Int <- G.newSized 2 2
+    G.write v 0 1
+    G.write v 1 2
+    v' <- G.freeze v
+    v' `shouldBe` [1, 2]
+  it "write-read indemponent" $ do
+    v :: GrowVector RealWorld Int <- G.newSized 1 1
+    G.write v 0 424242
+    a <- G.read v 0
+    a `shouldBe` 424242
+
+spec_ensure :: Spec
+spec_ensure = describe "capacity realloc" $ do
+  it "ensure reallocates properly" $ do
+    v :: GrowVector RealWorld Int <- G.new 5
+    G.ensure v 1
+    c1 <- G.capacity v
+    c1 `shouldBe` 5
+    G.ensure v 6
+    c2 <- G.capacity v
+    c2 `shouldBe` 6
+  it "ensureAppend reallocates properly" $ do
+    v :: GrowVector RealWorld Int <- G.new 5
+    G.ensureAppend v 1
+    c1 <- G.capacity v
+    c1 `shouldBe` 5
+    G.ensureAppend v 6
+    c2 <- G.capacity v
+    c2 `shouldBe` 8
+    G.ensureAppend v 14
+    c3 <- G.capacity v
+    c3 `shouldBe` 17
+
+spec_pushBack :: Spec
+spec_pushBack = describe "push back operations" $ do
+  it "push simple" $ do
+    v :: GrowVector RealWorld Int <- G.new 1
+    G.pushBack v 1
+    l1 <- G.length v
+    c1 <- G.capacity v
+    l1 `shouldBe` 1
+    c1 `shouldBe` 1
+    G.pushBack v 2
+    l2 <- G.length v
+    c2 <- G.capacity v
+    l2 `shouldBe` 2
+    c2 `shouldBe` 3
+    G.pushBack v 3
+    l3 <- G.length v
+    c3 <- G.capacity v
+    l3 `shouldBe` 3
+    c3 `shouldBe` 3
+    G.pushBack v 4
+    l4 <- G.length v
+    c4 <- G.capacity v
+    l4 `shouldBe` 4
+    c4 `shouldBe` 5
+    v' <- G.freeze v
+    v' `shouldBe` [1, 2, 3, 4]
+  it "push unsafe" $ do
+    v :: GrowVector RealWorld Int <- G.new 1
+    G.unsafePushBack v 1
+    l1 <- G.length v
+    c1 <- G.capacity v
+    l1 `shouldBe` 1
+    c1 `shouldBe` 1
+    G.ensureAppend v 1
+    G.unsafePushBack v 2
+    l2 <- G.length v
+    c2 <- G.capacity v
+    l2 `shouldBe` 2
+    c2 `shouldBe` 3
+    G.unsafePushBack v 3
+    l3 <- G.length v
+    c3 <- G.capacity v
+    l3 `shouldBe` 3
+    c3 `shouldBe` 3
+    G.ensureAppend v 1
+    G.unsafePushBack v 4
+    l4 <- G.length v
+    c4 <- G.capacity v
+    l4 `shouldBe` 4
+    c4 `shouldBe` 5
+    v' <- G.freeze v
+    v' `shouldBe` [1, 2, 3, 4]
