diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
-## [_Unreleased_](https://github.com/freckle/nonempty-zipper/compare/v1.0.0.4...main)
+## [_Unreleased_](https://github.com/freckle/nonempty-zipper/compare/v1.0.1.0...main)
 
-None
+## [v1.0.1.0](https://github.com/freckle/nonempty-zipper/compare/v1.0.0.4...v1.0.1.0)
+
+- Add `leftN` and `rightN`
 
 ## [v1.0.0.4](https://github.com/freckle/nonempty-zipper/compare/v1.0.0.3...v1.0.0.4)
 
diff --git a/library/Data/List/NonEmpty/Zipper.hs b/library/Data/List/NonEmpty/Zipper.hs
--- a/library/Data/List/NonEmpty/Zipper.hs
+++ b/library/Data/List/NonEmpty/Zipper.hs
@@ -6,24 +6,26 @@
 module Data.List.NonEmpty.Zipper
   ( Zipper
 
-  -- * Accessors
+    -- * Accessors
   , lefts
   , rights
   , current
 
-  -- * Traversal
+    -- * Traversal
   , left
   , right
+  , leftN
+  , rightN
   , findLeft
   , findRight
   , start
   , end
 
-  -- * Construction
+    -- * Construction
   , fromNonEmpty
   , fromNonEmptyEnd
 
-  -- ** Update
+    -- ** Update
   , replace
   , delete
   , push
@@ -32,7 +34,7 @@
   , unshift
   , reverse
 
-  -- * Predicates
+    -- * Predicates
   , isStart
   , isEnd
   )
@@ -43,6 +45,7 @@
 
 import Control.Comonad
 import Control.DeepSeq (NFData)
+import Control.Monad (foldM)
 import qualified Data.List.NonEmpty as NE
 import Data.Maybe (fromMaybe)
 import GHC.Generics (Generic)
@@ -69,18 +72,17 @@
 --
 -- >>> duplicate $ fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Zipper [] (Zipper [] 1 [2,3]) [Zipper [1] 2 [3],Zipper [2,1] 3 []]
---
 instance Comonad Zipper where
   extract = current
   duplicate z =
     let dupWith f r =
           case f r of
             Nothing -> [r]
-            Just x -> r:dupWith f x
-    in Zipper
-      (maybe [] (dupWith left) $ left z)
-      z
-      (maybe [] (dupWith right) $ right z)
+            Just x -> r : dupWith f x
+    in  Zipper
+          (maybe [] (dupWith left) $ left z)
+          z
+          (maybe [] (dupWith right) $ right z)
 
 -- | Get the current focus of the @'Zipper'@ cursor
 --
@@ -88,7 +90,6 @@
 --
 -- >>> current . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- 1
---
 current :: Zipper a -> a
 current (Zipper _ curr _) = curr
 
@@ -96,7 +97,6 @@
 --
 -- >>> lefts . fromNonEmptyEnd $ NE.fromList [1, 2, 3]
 -- [1,2]
---
 lefts :: Zipper a -> [a]
 lefts (Zipper ls _ _) = Prelude.reverse ls
 
@@ -104,16 +104,13 @@
 --
 -- >>> rights . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- [2,3]
---
 rights :: Zipper a -> [a]
 rights (Zipper _ _ rs) = rs
 
-
 -- | Move the current focus of the cursor to the left
 --
 -- >>> left . fromNonEmptyEnd $ NE.fromList [1, 2, 3]
 -- Just (Zipper [1] 2 [3])
---
 left :: Zipper a -> Maybe (Zipper a)
 left (Zipper ps curr ns) = do
   newCurr <- headMay ps
@@ -123,41 +120,60 @@
 --
 -- >>> right . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Just (Zipper [1] 2 [3])
---
 right :: Zipper a -> Maybe (Zipper a)
 right (Zipper ps curr ns) = do
   newCurr <- headMay ns
   pure $ Zipper (curr : ps) newCurr (fromMaybe [] $ tailMay ns)
 
--- | Move the current focus of the cursor to the first occurence of a value on the left
+-- | Move the current focus of the cursor some distance to the left
 --
+-- Returns 'Nothing' if the requested movement distance is too great
+-- and the cursor falls off the left end of the zipper.
+--
+-- >>> leftN 2 . fromNonEmptyEnd $ NE.fromList [1..7]
+-- Just (Zipper [4,3,2,1] 5 [6,7])
+leftN :: Word -> Zipper a -> Maybe (Zipper a)
+leftN = repeatedly left
+
+-- | Move the current focus of the cursor some distance to the right
+--
+-- Returns 'Nothing' if the requested movement distance is too great
+-- and the cursor falls off the right end of the zipper.
+--
+-- >>> rightN 2 . fromNonEmpty $ NE.fromList [1..7]
+-- Just (Zipper [2,1] 3 [4,5,6,7])
+rightN :: Word -> Zipper a -> Maybe (Zipper a)
+rightN = repeatedly right
+
+repeatedly :: Monad f => (a -> f a) -> Word -> a -> f a
+repeatedly f n x = foldM (const . f) x [1 .. n]
+
+-- | Move the current focus of the cursor to the first occurrence of a value on the left
+--
 -- >>> findLeft 2 . fromNonEmptyEnd $ NE.fromList [2, 1, 2, 1, 1, 3]
 -- Just (Zipper [1,2] 2 [1,1,3])
---
 findLeft :: Eq a => a -> Zipper a -> Maybe (Zipper a)
 findLeft target z@(Zipper ps curr ns)
   | curr == target = Just z
   | otherwise = case ps of
-    [] -> Nothing
-    (x : xs) -> findLeft target (Zipper xs x (curr : ns))
+      [] -> Nothing
+      (x : xs) -> findLeft target (Zipper xs x (curr : ns))
 
--- | Move the current focus of the cursor to the first occurence of a value on the right
+-- | Move the current focus of the cursor to the first occurrence of a value on the right
 --
 -- >>> findRight 3 . fromNonEmpty $ NE.fromList [2, 1, 3, 1, 1, 3]
 -- Just (Zipper [1,2] 3 [1,1,3])
---
 findRight :: Eq a => a -> Zipper a -> Maybe (Zipper a)
 findRight target z@(Zipper ps curr ns)
   | curr == target = Just z
   | otherwise = case ns of
-    [] -> Nothing
-    (x : xs) -> findRight target (Zipper (curr : ps) x xs)
+      [] -> Nothing
+      (x : xs) -> findRight target (Zipper (curr : ps) x xs)
 
 -- | Move the current focus of the cursor to the start of the @'Zipper'@
 --
 -- >>> start . fromNonEmptyEnd $ NE.fromList [1, 2, 3]
 -- Zipper [] 1 [2,3]
---
 start :: Zipper a -> Zipper a
 start z
   | isStart z = z
@@ -167,29 +183,26 @@
 --
 -- >>> end . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Zipper [2,1] 3 []
---
 end :: Zipper a -> Zipper a
 end z
   | isEnd z = z
   | otherwise = fromNonEmptyEnd $ toNonEmpty z
 
-
 fromNonEmpty :: NE.NonEmpty a -> Zipper a
 fromNonEmpty ne = Zipper [] (NE.head ne) (NE.tail ne)
 
 fromNonEmptyEnd :: NE.NonEmpty a -> Zipper a
 fromNonEmptyEnd ne = Zipper (NE.tail reversed) (NE.head reversed) []
-  where reversed = NE.reverse ne
+ where
+  reversed = NE.reverse ne
 
 toNonEmpty :: Zipper a -> NE.NonEmpty a
 toNonEmpty (Zipper ls x rs) = NE.fromList $ Prelude.reverse ls ++ [x] ++ rs
 
-
--- | Replace the current item under the curosr
+-- | Replace the current item under the cursor
 --
 -- >>> replace 4 . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Zipper [] 4 [2,3]
---
 replace :: a -> Zipper a -> Zipper a
 replace x (Zipper ls _ rs) = Zipper ls x rs
 
@@ -203,7 +216,6 @@
 --
 -- >>> delete . fromNonEmptyEnd $ NE.fromList [1, 2, 3]
 -- Just (Zipper [1] 2 [])
---
 delete :: Zipper a -> Maybe (Zipper a)
 delete (Zipper [] _ []) = Nothing
 delete (Zipper ls _ (r : rs)) = Just $ Zipper ls r rs
@@ -213,7 +225,6 @@
 --
 -- >>> push 0 . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Zipper [0] 1 [2,3]
---
 push :: a -> Zipper a -> Zipper a
 push l (Zipper ls x rs) = Zipper (l : ls) x rs
 
@@ -224,7 +235,6 @@
 --
 -- >>> pop . fromNonEmptyEnd $ NE.fromList [1, 2, 3]
 -- (Zipper [1] 3 [],Just 2)
---
 pop :: Zipper a -> (Zipper a, Maybe a)
 pop (Zipper [] x rs) = (Zipper [] x rs, Nothing)
 pop (Zipper (l : ls) x rs) = (Zipper ls x rs, Just l)
@@ -236,7 +246,6 @@
 --
 -- >>> shift . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- (Zipper [] 1 [3],Just 2)
---
 shift :: Zipper a -> (Zipper a, Maybe a)
 shift (Zipper ls x []) = (Zipper ls x [], Nothing)
 shift (Zipper ls x (r : rs)) = (Zipper ls x rs, Just r)
@@ -245,7 +254,6 @@
 --
 -- >>> unshift 4 . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Zipper [] 1 [4,2,3]
---
 unshift :: a -> Zipper a -> Zipper a
 unshift r (Zipper ls x rs) = Zipper ls x (r : rs)
 
@@ -253,11 +261,9 @@
 --
 -- >>> reverse . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- Zipper [2,3] 1 []
---
 reverse :: Zipper a -> Zipper a
 reverse (Zipper ls x rs) = Zipper rs x ls
 
-
 -- | Determine if the @'Zipper'@ is at the beginning
 --
 -- >>> isStart . fromNonEmpty $ NE.fromList [1, 2, 3]
@@ -265,7 +271,6 @@
 --
 -- >>> isStart . fromNonEmptyEnd $ NE.fromList [1, 2, 3]
 -- False
---
 isStart :: Zipper a -> Bool
 isStart (Zipper [] _ _) = True
 isStart _ = False
@@ -277,7 +282,6 @@
 --
 -- >>> isEnd . fromNonEmpty $ NE.fromList [1, 2, 3]
 -- False
---
 isEnd :: Zipper a -> Bool
 isEnd (Zipper _ _ []) = True
 isEnd _ = False
diff --git a/nonempty-zipper.cabal b/nonempty-zipper.cabal
--- a/nonempty-zipper.cabal
+++ b/nonempty-zipper.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               nonempty-zipper
-version:            1.0.0.4
+version:            1.0.1.0
 license:            MIT
 license-file:       LICENSE
 copyright:          2021 Renaissance Learning Inc
@@ -27,10 +27,10 @@
     default-language: Haskell2010
     ghc-options:      -Wall
     build-depends:
-        base >=4.11.1.0 && <5,
-        comonad >=5.0.4,
-        deepseq >=1.4.3.0,
-        safe >=0.3.17
+        base >=4.19.2.0 && <5,
+        comonad >=5.0.9,
+        deepseq >=1.5.1.0,
+        safe >=0.3.21
 
 test-suite doctest
     type:             exitcode-stdio-1.0
@@ -40,9 +40,9 @@
     default-language: Haskell2010
     ghc-options:      -Wall
     build-depends:
-        Glob >=0.9.3,
-        base >=4.11.1.0 && <5,
-        comonad >=5.0.4,
-        deepseq >=1.4.3.0,
+        Glob >=0.10.2,
+        base >=4.19.2.0 && <5,
+        comonad >=5.0.9,
+        deepseq >=1.5.1.0,
         doctest >0.16,
-        safe >=0.3.17
+        safe >=0.3.21
