diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
 Brick changelog
 ---------------
 
+2.13
+----
+
+New features:
+
+* Brick.Widgets.List: added support for wrapping (thanks Enrico Maria De
+  Angelis). The List API now provides `setScrollWrap` to configure lists
+  to wrap when moving their cursor, and the cursor-movement functions
+  and event handlers now cause selection wrapping when a list has
+  wrapping enabled. The `ListDemo` demo program was also updated to
+  demonstrate the wrapping behavior.
+
 2.12
 ----
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -215,7 +215,7 @@
 should consider to make submitting patches easier for all concerned:
 
  - Patches written completely or partially by AI are unlikely to be
-   accepted.
+   accepted. Please disclose any AI use.
  - If you want to take on big things, talk to me first; let's have a
    design/vision discussion before you start coding. Create a GitHub
    issue and we can use that as the place to hash things out.
diff --git a/brick.cabal b/brick.cabal
--- a/brick.cabal
+++ b/brick.cabal
@@ -1,5 +1,5 @@
 name:                brick
-version:             2.12
+version:             2.13
 synopsis:            A declarative terminal user interface library
 description:
   Write terminal user interfaces (TUIs) painlessly with 'brick'! You
diff --git a/programs/ListDemo.hs b/programs/ListDemo.hs
--- a/programs/ListDemo.hs
+++ b/programs/ListDemo.hs
@@ -43,10 +43,16 @@
               hLimit 25 $
               vLimit 15 $
               L.renderList listDrawElement True l
+        wrapStatus = if L.getScrollWrap l
+                     then "enabled"
+                     else "disabled"
         ui = C.vCenter $ vBox [ C.hCenter box
                               , str " "
                               , C.hCenter $ str "Press +/- to add/remove list elements."
                               , C.hCenter $ str "Press Esc to exit."
+                              , str " "
+                              , C.hCenter $ str "Press 'w' to toggle selection wrapping."
+                              , C.hCenter $ str $ "Selection wrapping is currently " <> wrapStatus <> "."
                               ]
 
 appEvent :: T.BrickEvent () e -> T.EventM () (L.List () Char) ()
@@ -64,6 +70,9 @@
                 Nothing -> return ()
                 Just i -> modify $ L.listRemove i
 
+        V.EvKey (V.KChar 'w') [] ->
+            toggleListWrapping
+
         V.EvKey V.KEsc [] -> M.halt
 
         ev -> L.handleListEvent ev
@@ -71,6 +80,9 @@
       nextElement :: Vec.Vector Char -> Char
       nextElement v = fromMaybe '?' $ Vec.find (flip Vec.notElem v) (Vec.fromList ['a' .. 'z'])
 appEvent _ = return ()
+
+toggleListWrapping :: T.EventM () (L.List () Char) ()
+toggleListWrapping = L.listScrollWrapL %= not
 
 listDrawElement :: (Show a) => Bool -> a -> Widget ()
 listDrawElement sel a =
diff --git a/src/Brick/Widgets/List.hs b/src/Brick/Widgets/List.hs
--- a/src/Brick/Widgets/List.hs
+++ b/src/Brick/Widgets/List.hs
@@ -22,6 +22,11 @@
   -- * Constructing a list
   , list
 
+  -- * Configuring wrapping
+  , setScrollWrap
+  , getScrollWrap
+  , listScrollWrapL
+
   -- * Rendering a list
   , renderList
   , renderListWithIndex
@@ -103,8 +108,8 @@
 import Brick.AttrMap
 
 -- | List state. Lists have a container @t@ of element type @e@ that is
--- the data stored by the list. Internally, Lists handle the following
--- events by default:
+-- the data stored by the list. When using the event-handling functions
+-- provided by this module, Lists handle the following events:
 --
 -- * Up/down arrow keys: move cursor of selected item
 -- * Page up / page down keys: move cursor of selected item by one page
@@ -112,6 +117,15 @@
 -- * Home/end keys: move cursor of selected item to beginning or end of
 --   list
 --
+-- Movement key behaviors (and their corresponding list transformation
+-- functions) are subject to wrapping if the list's wrapping is enabled;
+-- in that case, attempts to move the selection beyond either end of the
+-- list will wrap the selection to the opposite end of the list. When
+-- wrapping is disabled, attempts to move beyond either end of the list
+-- will move the selection as far as possible without wrapping around
+-- to the opposite end. To control whether wrapping is enabled, see
+-- 'setScrollWrap'.
+--
 -- The 'List' type synonym fixes @t@ to 'V.Vector' for compatibility
 -- with previous versions of this library.
 --
@@ -123,7 +137,6 @@
 -- * 'listRemove': 'Semigroup'
 -- * 'listClear': 'Monoid'
 -- * 'listReverse': 'Reversible'
---
 data GenericList n t e =
     List { listElements :: !(t e)
          -- ^ The list's sequence of elements.
@@ -133,6 +146,9 @@
          -- ^ The list's name.
          , listItemHeight :: Int
          -- ^ The height of an individual item in the list.
+         , listScrollWrap :: Bool
+         -- ^ Whether moving beyond a list's first/last element
+         -- should wrap to the last/first element.
          } deriving (Functor, Foldable, Traversable, Show, Generic)
 
 suffixLenses ''GenericList
@@ -263,7 +279,8 @@
 listSelectedFocusedAttr :: AttrName
 listSelectedFocusedAttr = listSelectedAttr <> attrName "focused"
 
--- | Construct a list in terms of container 't' with element type 'e'.
+-- | Construct a list, with wrapping initially disabled, in terms of
+-- container 't' with element type 'e'.
 list :: (Foldable t)
      => n
      -- ^ The list name (must be unique)
@@ -276,7 +293,7 @@
 list name es h =
     let selIndex = if null es then Nothing else Just 0
         safeHeight = max 1 h
-    in List es selIndex name safeHeight
+    in List es selIndex name safeHeight False
 
 -- | Render a list using the specified item drawing function.
 --
@@ -465,8 +482,8 @@
             | otherwise = 0
     in l' & listSelectedL .~ newSel
 
--- | Move the list selected index up by one. (Moves the cursor up,
--- subtracts one from the index.)
+-- | Move the list selected index up by one (moves the cursor up,
+-- subtracts one from the index), subject to wrapping.
 listMoveUp :: (Foldable t, Splittable t)
            => GenericList n t e
            -> GenericList n t e
@@ -477,8 +494,8 @@
                => EventM n (GenericList n t e) ()
 listMovePageUp = listMoveByPages (-1::Double)
 
--- | Move the list selected index down by one. (Moves the cursor down,
--- adds one to the index.)
+-- | Move the list selected index down by one (moves the cursor down,
+-- adds one to the index), subject to wrapping.
 listMoveDown :: (Foldable t, Splittable t)
              => GenericList n t e
              -> GenericList n t e
@@ -489,7 +506,8 @@
                  => EventM n (GenericList n t e) ()
 listMovePageDown = listMoveByPages (1::Double)
 
--- | Move the list selected index by some (fractional) number of pages.
+-- | Move the list selected index by some (fractional) number of pages,
+-- subject to wrapping.
 listMoveByPages :: (Foldable t, Splittable t, Ord n, RealFrac m)
                 => m
                 -> EventM n (GenericList n t e) ()
@@ -506,8 +524,9 @@
 -- | Move the list selected index.
 --
 -- If the current selection is @Just x@, the selection is adjusted by
--- the specified amount. The value is clamped to the extents of the list
--- (i.e. the selection does not "wrap").
+-- the specified amount. Whether the value is clamped to the extents of the
+-- list or not (i.e. whether the selection doesn't wrap around the list or it
+-- does), is determined by the list itself (see 'GenericList' for more info).
 --
 -- If the current selection is @Nothing@ (i.e. there is no selection)
 -- and the direction is positive, set to @Just 0@ (first element),
@@ -528,7 +547,10 @@
             Nothing
                 | amt > 0 -> 0
                 | otherwise -> length l - 1
-            Just i -> max 0 (amt + i)  -- don't be negative
+            Just i
+                | let wrap = l ^. listScrollWrapL
+                , wrap -> (amt + i) `mod` length l
+                | otherwise -> max 0 (amt + i)  -- don't be negative
     in listMoveTo target l
 
 -- | Set the selected index for a list to the specified index, subject
@@ -623,7 +645,6 @@
 --                                O(n) -- set, modify, traverse
 -- listSelectedElementL for 'Seq.Seq': O(log(min(i, n - i)))  -- all operations
 -- @
---
 listSelectedElementL :: (Splittable t, Traversable t, Semigroup (t e))
                      => Traversal' (GenericList n t e) e
 listSelectedElementL f l =
@@ -674,8 +695,8 @@
     l & listElementsL %~ reverse
       & listSelectedL %~ fmap (length l - 1 -)
 
--- | Apply a function to the selected element. If no element is selected
--- the list is not modified.
+-- | Apply a function to the selected element. If no element is
+-- selected, the list is not modified.
 --
 -- Complexity: same as 'traverse' for the container type (typically
 -- /O(n)/).
@@ -686,9 +707,19 @@
 -- listModify for 'List': O(n)
 -- listModify for 'Seq.Seq': O(log(min(i, n - i)))
 -- @
---
 listModify :: (Traversable t, Splittable t, Semigroup (t e))
            => (e -> e)
            -> GenericList n t e
            -> GenericList n t e
 listModify f = listSelectedElementL %~ f
+
+-- | Sets the list's wrapping behavior; wrapping is enabled if given
+-- @True@, or disabled if given @False@.
+setScrollWrap :: (Traversable t, Splittable t, Semigroup (t e))
+              => Bool -> GenericList n t e -> GenericList n t e
+setScrollWrap b = listScrollWrapL .~ b
+
+-- | Returns the list's wrapping setting; returns @True@ if wrapping is
+-- enabled or @False@ otherwise.
+getScrollWrap :: GenericList n t e -> Bool
+getScrollWrap = listScrollWrap
diff --git a/tests/List.hs b/tests/List.hs
--- a/tests/List.hs
+++ b/tests/List.hs
@@ -220,6 +220,20 @@
         len = length l''
     in maybe (len == 0) (== 0) (l'' ^. listSelectedL)
 
+-- listMoveUp from beginning is the same as listMoveToEnd
+prop_moveBeforeFirst :: Eq a => List n a -> Bool
+prop_moveBeforeFirst l =
+    let l' = setScrollWrap True l
+    in listMoveUp (listMoveToBeginning l') =.= listMoveToEnd l'
+  where (=.=) = (==) `on` (^. listSelectedL)
+
+-- listMoveDown from end is the same as listMoveToBeginning
+prop_moveAfterLast :: Eq a => List n a -> Bool
+prop_moveAfterLast l =
+    let l' = setScrollWrap True l
+    in listMoveDown (listMoveToEnd l') =.= listMoveToBeginning l'
+  where (=.=) = (==) `on` (^. listSelectedL)
+
 -- listMoveDown always reaches end of list (or list is empty)
 prop_moveDown :: (Eq a) => [ListOp a] -> List n a -> Bool
 prop_moveDown ops l =
