diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for brick-tabular-list
 
+## 0.2.0.1 -- 2023-02-27
+
+* Fixed demo programs for haskell-language-server
+
 ## 0.2.0.0 -- 2023-02-20
 
 * Add drawColHdrRowHdr
diff --git a/brick-tabular-list.cabal b/brick-tabular-list.cabal
--- a/brick-tabular-list.cabal
+++ b/brick-tabular-list.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: brick-tabular-list
-version: 0.2.0.0
+version: 0.2.0.1
 
 synopsis: Tabular list widgets for brick.
 
@@ -13,7 +13,7 @@
     == To get started
 
     * Read "Brick.Widgets.TabularList.Grid" or "Brick.Widgets.TabularList.Mixed".
-    * Run demo programs in demos/ directory. To learn more quickly, modify and run demo programs.
+    * Run demo programs. To learn more quickly, modify and run demo programs.
 
     == Lens support
 
@@ -39,30 +39,19 @@
     type: git
     location: https://codeberg.org/amano.kenji/brick-tabular-list.git
 
-Flag demos
+Flag demo
     Description: Build demonstration programs
     Default: False
 
-common all
+library
     build-depends:
         base >=4.15.1.0 && <5
       , brick >=1.5 && <1.7
       , containers ^>=0.6.4
+      , generic-lens ^>=2.2.1
       , microlens ^>=0.4.13
       , optics-core ^>=0.4.1
       , vty ^>=5.38
-    default-language: Haskell2010
-
-common exec
-    import: all
-    build-depends: brick-tabular-list
-    if !flag(demos)
-      buildable: False
-    hs-source-dirs: demos
-    ghc-options: -threaded
-
-library
-    import: all
     exposed-modules:
         Brick.Widgets.TabularList
       , Brick.Widgets.TabularList.Grid
@@ -71,30 +60,48 @@
     other-modules:
         Brick.Widgets.TabularList.Internal.Common
       , Brick.Widgets.TabularList.Internal.Lens
-    build-depends:
-        generic-lens ^>=2.2.1
     hs-source-dirs: src
+    default-language: Haskell2010
 
-common mixed-tabular-list
-    import: exec
-    other-modules: Internal.MixedTabularList
+library demo
+    if !flag(demo)
+      buildable: False
+    build-depends:
+        base
+      , brick
+      , brick-tabular-list
+      , containers
+      , optics-core
+      , vty
+    exposed-modules:
+        Internal.MixedTabularList
+      , Internal.GridTabularList
+    hs-source-dirs: demo
+    default-language: Haskell2010
 
+common exec
+    if !flag(demo)
+      buildable: False
+    build-depends:
+        base
+      , brick-tabular-list
+      , demo
+    hs-source-dirs: exec
+    ghc-options: -threaded
+    default-language: Haskell2010
+
 executable mixed-tabular-list
-    import: mixed-tabular-list
-    main-is:          MixedTabularList.hs
+    import: exec
+    main-is: MixedTabularList.hs
 
 executable mixed-tabular-list-vi
-    import: mixed-tabular-list
-    main-is:          MixedTabularListVi.hs
-
-common grid-tabular-list
     import: exec
-    other-modules: Internal.GridTabularList
+    main-is: MixedTabularListVi.hs
 
 executable grid-tabular-list
-    import: grid-tabular-list
+    import: exec
     main-is: GridTabularList.hs
 
 executable grid-tabular-list-vi
-    import: grid-tabular-list
+    import: exec
     main-is: GridTabularListVi.hs
diff --git a/demo/Internal/GridTabularList.hs b/demo/Internal/GridTabularList.hs
new file mode 100644
--- /dev/null
+++ b/demo/Internal/GridTabularList.hs
@@ -0,0 +1,182 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE FlexibleContexts #-}
+module Internal.GridTabularList (
+  runMain
+, theList
+)
+where
+
+import Brick.Widgets.TabularList.Grid hiding (sizes, contents)
+-- base
+import GHC.Generics (Generic)
+import Control.Monad (void)
+-- Third party libraries
+import Optics.Core
+import Data.Sequence (Seq(..))
+import qualified Data.Sequence as S
+-- brick
+import Brick.Main
+import Brick.AttrMap
+import Brick.Types
+import Brick.Widgets.Core
+import Brick.Widgets.Center
+import Brick.Widgets.Border
+import Brick.Util
+import Brick.Widgets.List
+import Brick.Widgets.Border.Style
+import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)
+
+data Song = Song {
+  artist :: String
+, title :: String
+, album :: String
+, composer :: String
+, genre :: String
+, time :: Int
+} deriving Generic
+
+data SongCell = StringCell String | TimeCell Int deriving Generic
+
+data Name = TheList deriving (Eq, Ord, Show)
+
+type LibraryList = GridTabularList Name Song SongCell Int String
+type LibraryRenderers = GridRenderers Name Song SongCell Int String
+type LibraryContents = GridContents Name Song SongCell Int String
+type LibraryEventHandler = LibraryRenderers -> Event -> EventM Name LibraryList ()
+
+songs :: Seq Song
+songs = S.fromList $
+  map (\n -> Song ("Artist " <> show n) ("Title " <> show n)
+                  ("Album " <> show n) ("Composer " <> show n) ("Genre " <> show n)
+                  n)
+      [1..1000]
+
+sizes :: GridSizes Int
+sizes = GridSizes {
+  row = S.fromList [12, 11, 11, 14, 11, 7]
+, rowHdr = Just $ VisibleRowHeaders $ \_ rowHs -> (+2) $ maximum $ map (length . show) rowHs
+, colHdr = Just 1
+}
+
+contents :: LibraryContents
+contents = let
+  getCell s 0 = Just $ StringCell $ s ^. #artist
+  getCell s 1 = Just $ StringCell $ s ^. #title
+  getCell s 2 = Just $ StringCell $ s ^. #album
+  getCell s 3 = Just $ StringCell $ s ^. #composer
+  getCell s 4 = Just $ StringCell $ s ^. #genre
+  getCell s 5 = Just $ TimeCell $ s ^. #time
+  getCell _ _ = Nothing
+  getColumnHeader 0 = Just "Artist"
+  getColumnHeader 1 = Just "Title"
+  getColumnHeader 2 = Just "Album"
+  getColumnHeader 3 = Just "Composer"
+  getColumnHeader 4 = Just "Genre"
+  getColumnHeader 5 = Just "Time"
+  getColumnHeader _ = Nothing
+  getRowHeader _ n = Just (n+1)
+  in GridContents {
+    cell = getCell
+  , rowHdr = Just getRowHeader
+  , colHdr = Just getColumnHeader
+  }
+
+data AppState = AppState {
+  libList :: LibraryList
+, libRenderers :: LibraryRenderers
+, listWidth :: Int
+} deriving Generic
+
+handleLibEvent :: LibraryEventHandler -> BrickEvent Name () -> EventM Name AppState ()
+handleLibEvent eh e = do
+  s <- get
+  case e of
+    VtyEvent (EvKey KEsc []) -> halt
+    VtyEvent (EvKey (KChar 'q') []) -> halt
+    VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #drawColHdr) $ modify $ \case
+      Nothing -> Just dch
+      Just _ -> Nothing
+    VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #drawRowHdr) $ modify $ \case
+      Nothing -> Just drh
+      Just _ -> Nothing
+    VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1
+    VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)
+    VtyEvent e -> zoom #libList $ eh (s ^. #libRenderers) e
+    _ -> return ()
+
+drawUi :: [String] -> AppState -> [Widget Name]
+drawUi msgs s = let
+  theList = renderGridTabularList (s ^. #libRenderers) True (s ^. #libList)
+  in [vCenter $ vBox $ hCenter (padLeftRight 2 $ border $ hLimit (s ^. #listWidth) $ vLimit 15 theList)
+                     : hCenter (str "Press Esc or q to exit" )
+                     : hCenter (str "Press c to toggle column headers")
+                     : hCenter (str "Press r to toggle row headaers")
+                     : hCenter (str "Press - to shorten the list and = to widen the list")
+                     : hCenter (str " ")
+                     : map (hCenter . str) msgs]
+
+columnHdrAttr :: AttrName
+columnHdrAttr = attrName "columnHeader"
+rowHdrAttr :: AttrName
+rowHdrAttr = attrName "rowHeader"
+colSelectedAttr :: AttrName
+colSelectedAttr = attrName "selectedColumn"
+
+getApp :: [String] -> LibraryEventHandler -> App AppState () Name
+getApp msgs eh = App {
+  appDraw = drawUi msgs
+, appChooseCursor = neverShowCursor
+, appHandleEvent = handleLibEvent eh
+, appStartEvent = return ()
+, appAttrMap = const $ attrMap defAttr [ (colSelectedAttr, black `on` white)
+                                       , (columnHdrAttr, fg blue)
+                                       , (rowHdrAttr, fg red)]
+}
+
+drh lf wd (Position i f) row = \case
+  Nothing -> fill ' '
+  Just rh ->
+    let attrFn = if f
+          then id
+          else withAttr rowHdrAttr
+    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)
+
+dch lf wd (Position i f) = \case
+  Nothing -> fill ' '
+  Just ch -> withAttr columnHdrAttr $ padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str ch)
+
+renderers :: LibraryRenderers
+renderers = GridRenderers {
+  drawCell = \lf wd gc song mc -> let
+    attrFn = if gc ^. #row % #selected && gc ^. #col % #selected 
+      then withAttr colSelectedAttr
+      else id
+    in attrFn $ case mc of
+      Nothing -> fill ' '
+      Just (StringCell s) -> padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str s)
+      Just (TimeCell time) -> let
+        (min, sec) = time `divMod` 60
+        time' = case min of
+          0 -> show sec
+          _ -> show min <> ":" <> show sec
+        in padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max $ str time'
+, drawRowHdr = Just drh
+, drawColHdr = Just dch
+-- This is the same as Nothing.
+, drawColHdrRowHdr = Just $ \_ _ -> fill ' '
+}
+
+theList :: LibraryList
+theList = gridTabularList TheList songs 1 sizes contents
+
+runMain :: [String] -> LibraryEventHandler -> IO ()
+runMain msgs eh = do
+  let appState = AppState {
+        libList = theList
+      , libRenderers = renderers
+      , listWidth = 39
+      }
+  void $ defaultMain (getApp msgs eh) appState
diff --git a/demo/Internal/MixedTabularList.hs b/demo/Internal/MixedTabularList.hs
new file mode 100644
--- /dev/null
+++ b/demo/Internal/MixedTabularList.hs
@@ -0,0 +1,176 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedLabels #-}
+module Internal.MixedTabularList (
+  runMain
+)
+where
+
+import Brick.Widgets.TabularList.Mixed hiding (sizes, contents)
+-- base
+import GHC.Generics (Generic)
+import Control.Monad (void)
+-- Third party libraries
+import Optics.Core
+import Data.Sequence (Seq(..))
+import qualified Data.Sequence as S
+-- brick
+import Brick.Main
+import Brick.AttrMap
+import Brick.Types
+import Brick.Widgets.Core
+import Brick.Widgets.Center
+import Brick.Widgets.Border
+import Brick.Util
+import Brick.Widgets.List
+import Brick.Widgets.Border.Style
+import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)
+
+data Song = Song {
+  artist :: String
+, title :: String
+, album :: String
+, time :: Int
+} deriving Generic
+
+data LibraryEntry = LibFolder String | LibSong Song deriving Generic
+
+data LibCell = StringCell String | FolderCell String | TimeCell Int deriving Generic
+
+data Name = TheList deriving (Eq, Ord, Show)
+
+type LibraryList = MixedTabularList Name LibraryEntry LibCell Int String
+type LibraryRenderers = MixedRenderers Name LibraryEntry LibCell Int String
+type LibraryContents = MixedContents LibraryEntry LibCell Int String
+type LibraryEventHandler = Event -> EventM Name LibraryList ()
+
+libraryEntries :: Seq LibraryEntry
+libraryEntries = let
+  folders = [LibFolder "[.]", LibFolder "[..]"]
+  songs = map (\n -> LibSong (Song ("Artist " <> show n) ("Title " <> show n) ("Album " <> show n) n)) [1..1000]
+  in S.fromList $ folders ++ songs
+
+contents :: LibraryContents
+contents = let
+  getCell (LibFolder s) 0 = Just $ FolderCell $ "Folder: " <> s
+  getCell (LibFolder _) _ = Nothing
+  getCell (LibSong s) 0 = Just $ StringCell $ s ^. #artist
+  getCell (LibSong s) 1 = Just $ StringCell $ s ^. #title
+  getCell (LibSong s) 2 = Just $ StringCell $ s ^. #album
+  getCell (LibSong s) 3 = Just $ TimeCell $ s ^. #time
+  getCell (LibSong _) _ = Nothing
+  getColumnHeader 0 = Just "Artist"
+  getColumnHeader 1 = Just "Title"
+  getColumnHeader 2 = Just "Album"
+  getColumnHeader 3 = Just "Time"
+  getColumnHeader _ = Nothing
+  getRowHeader _ n = Just (n+1)
+  in MixedContents {
+    cell = getCell
+  , rowHdr = Just getRowHeader
+  , colHdr = Just getColumnHeader
+  }
+
+data AppState = AppState {
+  libList :: LibraryList
+, libRenderers :: LibraryRenderers
+, listWidth :: Int
+} deriving Generic
+
+handleLibEvent :: LibraryEventHandler -> BrickEvent Name () -> EventM Name AppState ()
+handleLibEvent eh e = case e of
+  VtyEvent (EvKey KEsc []) -> halt
+  VtyEvent (EvKey (KChar 'q') []) -> halt
+  VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #drawColHdr) $ modify $ \case
+    Nothing -> Just dch
+    Just _ -> Nothing
+  VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #drawRowHdr) $ modify $ \case
+    Nothing -> Just drh
+    Just _ -> Nothing
+  VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1
+  VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)
+  VtyEvent e -> zoom #libList $ eh e
+  _ -> return ()
+  where abc = []
+
+drawUi :: [String] -> AppState -> [Widget Name]
+drawUi msgs s = let
+  theList = renderMixedTabularList (s ^. #libRenderers) True (s ^. #libList)
+  in [vCenter $ vBox $ hCenter (border $ vLimit 15 $ hLimit (s ^. #listWidth) theList)
+                     : hCenter (str "Press Esc or q to exit" )
+                     : hCenter (str "Press c to toggle column headers")
+                     : hCenter (str "Press r to toggle row headaers")
+                     : hCenter (str "Press - to shorten the list and = to widen the list")
+                     : hCenter (str " ")
+                     : map (hCenter . str) msgs ]
+
+columnHdrAttr :: AttrName
+columnHdrAttr = attrName "columnHeader"
+rowHdrAttr :: AttrName
+rowHdrAttr = attrName "rowHeader"
+
+getApp :: [String] -> LibraryEventHandler -> App AppState () Name
+getApp msgs eh = App {
+  appDraw = drawUi msgs
+, appChooseCursor = neverShowCursor
+, appHandleEvent = handleLibEvent eh
+, appStartEvent = return ()
+, appAttrMap = const $ attrMap defAttr [ (listSelectedAttr, black `on` white)
+                                       , (columnHdrAttr, fg blue)
+                                       , (rowHdrAttr, fg red)]
+}
+
+dch lf ci = \case
+  Nothing -> hCenter $ str " "
+  Just ch -> withAttr columnHdrAttr $ padRight Max (str ch) <+> str " "
+
+drh lf wd (Position i f) row = \case
+  Nothing -> hCenter $ str " "
+  Just rh ->
+    let attrFn = if f
+          then id
+          else withAttr rowHdrAttr
+    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)
+
+renderers :: LibraryRenderers
+renderers = MixedRenderers {
+  drawCell = \lf (Position i f) row mc -> case mc of
+    Nothing -> hCenter $ str " "
+    Just (StringCell s) -> padRight Max (str s) <+> str " "
+    Just (FolderCell s) -> padRight Max $ str s
+    Just (TimeCell time) -> let
+      (min, sec) = time `divMod` 60
+      time' = case min of
+        0 -> show sec
+        _ -> show min <> ":" <> show sec
+      in padRight Max (str time') <+> str " "
+, drawRowHdr = Just drh
+, drawColHdr = Just dch
+-- This is the same as Nothing.
+, drawColHdrRowHdr = Just $ \_ _ -> fill ' '
+}
+
+runMain :: [String] -> LibraryEventHandler -> IO ()
+runMain msgs eh = do
+  let sizes = MixedSizes {
+        rowHdr = Just $ VisibleRowHeaders $ \_ rowHs -> (+2) $ maximum $ map (length . show) rowHs
+      , colSizes = AvailWidth $ \aW -> let
+          artist = max 7 $ (aW * 30) `div` 100
+          title = max 6 $ (aW * 30) `div` 100
+          time = 7
+          album = aW - artist - title - time
+          songWidths = [artist, title, album, time]
+          in ColSizes {
+            rowKind = \case
+              LibFolder _ -> [aW]
+              LibSong _ -> songWidths
+          , colHdr = Just (songWidths, 1)
+          }
+      }
+      appState = AppState {
+        libList = mixedTabularList TheList libraryEntries 1 sizes contents
+      , libRenderers = renderers
+      , listWidth = 80
+      }
+  void $ defaultMain (getApp msgs eh) appState
diff --git a/demos/GridTabularList.hs b/demos/GridTabularList.hs
deleted file mode 100644
--- a/demos/GridTabularList.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module Main where
-
-import Internal.GridTabularList
-import Brick.Widgets.TabularList.Grid
-
-main :: IO ()
-main = do
-  let msgs = [ "Press Up arrow to go up one item"
-             , "Press Down arrow to go down one item"
-             , "Press PageUp to go up one page"
-             , "Press PageDown to go down one page"
-             , "Press Home to go to the beginning"
-             , "Press End to go to the end" 
-             , " "
-             , "Press Left arrow to go left by one column"
-             , "Press Right arrow to go right by one column"
-             , "Press Ctrl+Home to go to the first column"
-             , "Press Ctrl+End to go to the last column"
-             , "Press Ctrl+PageUp to go left one page of columns"
-             , "Press Ctrl+PageDown to go right one page of columns" ]
-  runMain msgs handleGridListEvent
diff --git a/demos/GridTabularListVi.hs b/demos/GridTabularListVi.hs
deleted file mode 100644
--- a/demos/GridTabularListVi.hs
+++ /dev/null
@@ -1,25 +0,0 @@
-module Main where
-
-import Internal.GridTabularList
-import Brick.Widgets.TabularList.Grid
-
-main :: IO ()
-main = do
-  let msgs = [ "Press Up arrow or k to go up one item"
-             , "Press Down arrow or j to go down one item"
-             , "Press PageUp or Ctrl+f to go up one page"
-             , "Press PageDown or Ctrl+b to go down one page"
-             , "Press Home or g to go to the beginning"
-             , "Press End or G to go to the end" 
-             , "Press Ctrl+u to go up half page"
-             , "Press Ctrl+d to go down half page"
-             , " "
-             , "Press Left arrow or h to go left by one column"
-             , "Press Right arrow or l to go right by one column"
-             , "Press Ctrl+Home or H to go to the first column"
-             , "Press Ctrl+End or L to go to the last column"
-             , "Press Ctrl+PageUp or Alt+h or Backspace to go left by one page of columns"
-             , "Press Ctrl+PageDown or Alt+l to go right by one page of columns" ]
-  runMain msgs $ \r e -> do
-    handleGridListEvent r e
-    handleGridListEventVi r e
diff --git a/demos/Internal/GridTabularList.hs b/demos/Internal/GridTabularList.hs
deleted file mode 100644
--- a/demos/Internal/GridTabularList.hs
+++ /dev/null
@@ -1,182 +0,0 @@
-{-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE FlexibleContexts #-}
-module Internal.GridTabularList (
-  runMain
-, theList
-)
-where
-
-import Brick.Widgets.TabularList.Grid hiding (sizes, contents)
--- base
-import GHC.Generics (Generic)
-import Control.Monad (void)
--- Third party libraries
-import Optics.Core
-import Data.Sequence (Seq(..))
-import qualified Data.Sequence as S
--- brick
-import Brick.Main
-import Brick.AttrMap
-import Brick.Types
-import Brick.Widgets.Core
-import Brick.Widgets.Center
-import Brick.Widgets.Border
-import Brick.Util
-import Brick.Widgets.List
-import Brick.Widgets.Border.Style
-import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)
-
-data Song = Song {
-  artist :: String
-, title :: String
-, album :: String
-, composer :: String
-, genre :: String
-, time :: Int
-} deriving Generic
-
-data SongCell = StringCell String | TimeCell Int deriving Generic
-
-data Name = TheList deriving (Eq, Ord, Show)
-
-type LibraryList = GridTabularList Name Song SongCell Int String
-type LibraryRenderers = GridRenderers Name Song SongCell Int String
-type LibraryContents = GridContents Name Song SongCell Int String
-type LibraryEventHandler = LibraryRenderers -> Event -> EventM Name LibraryList ()
-
-songs :: Seq Song
-songs = S.fromList $
-  map (\n -> Song ("Artist " <> show n) ("Title " <> show n)
-                  ("Album " <> show n) ("Composer " <> show n) ("Genre " <> show n)
-                  n)
-      [1..1000]
-
-sizes :: GridSizes Int
-sizes = GridSizes {
-  row = S.fromList [12, 11, 11, 14, 11, 7]
-, rowHdr = Just $ VisibleRowHeaders $ \_ rowHs -> (+2) $ maximum $ map (length . show) rowHs
-, colHdr = Just 1
-}
-
-contents :: LibraryContents
-contents = let
-  getCell s 0 = Just $ StringCell $ s ^. #artist
-  getCell s 1 = Just $ StringCell $ s ^. #title
-  getCell s 2 = Just $ StringCell $ s ^. #album
-  getCell s 3 = Just $ StringCell $ s ^. #composer
-  getCell s 4 = Just $ StringCell $ s ^. #genre
-  getCell s 5 = Just $ TimeCell $ s ^. #time
-  getCell _ _ = Nothing
-  getColumnHeader 0 = Just "Artist"
-  getColumnHeader 1 = Just "Title"
-  getColumnHeader 2 = Just "Album"
-  getColumnHeader 3 = Just "Composer"
-  getColumnHeader 4 = Just "Genre"
-  getColumnHeader 5 = Just "Time"
-  getColumnHeader _ = Nothing
-  getRowHeader _ n = Just (n+1)
-  in GridContents {
-    cell = getCell
-  , rowHdr = Just getRowHeader
-  , colHdr = Just getColumnHeader
-  }
-
-data AppState = AppState {
-  libList :: LibraryList
-, libRenderers :: LibraryRenderers
-, listWidth :: Int
-} deriving Generic
-
-handleLibEvent :: LibraryEventHandler -> BrickEvent Name () -> EventM Name AppState ()
-handleLibEvent eh e = do
-  s <- get
-  case e of
-    VtyEvent (EvKey KEsc []) -> halt
-    VtyEvent (EvKey (KChar 'q') []) -> halt
-    VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #drawColHdr) $ modify $ \case
-      Nothing -> Just dch
-      Just _ -> Nothing
-    VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #drawRowHdr) $ modify $ \case
-      Nothing -> Just drh
-      Just _ -> Nothing
-    VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1
-    VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)
-    VtyEvent e -> zoom #libList $ eh (s ^. #libRenderers) e
-    _ -> return ()
-
-drawUi :: [String] -> AppState -> [Widget Name]
-drawUi msgs s = let
-  theList = renderGridTabularList (s ^. #libRenderers) True (s ^. #libList)
-  in [vCenter $ vBox $ hCenter (padLeftRight 2 $ border $ hLimit (s ^. #listWidth) $ vLimit 15 theList)
-                     : hCenter (str "Press Esc or q to exit" )
-                     : hCenter (str "Press c to toggle column headers")
-                     : hCenter (str "Press r to toggle row headaers")
-                     : hCenter (str "Press - to shorten the list and = to widen the list")
-                     : hCenter (str " ")
-                     : map (hCenter . str) msgs]
-
-columnHdrAttr :: AttrName
-columnHdrAttr = attrName "columnHeader"
-rowHdrAttr :: AttrName
-rowHdrAttr = attrName "rowHeader"
-colSelectedAttr :: AttrName
-colSelectedAttr = attrName "selectedColumn"
-
-getApp :: [String] -> LibraryEventHandler -> App AppState () Name
-getApp msgs eh = App {
-  appDraw = drawUi msgs
-, appChooseCursor = neverShowCursor
-, appHandleEvent = handleLibEvent eh
-, appStartEvent = return ()
-, appAttrMap = const $ attrMap defAttr [ (colSelectedAttr, black `on` white)
-                                       , (columnHdrAttr, fg blue)
-                                       , (rowHdrAttr, fg red)]
-}
-
-drh lf wd (Position i f) row = \case
-  Nothing -> fill ' '
-  Just rh ->
-    let attrFn = if f
-          then id
-          else withAttr rowHdrAttr
-    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)
-
-dch lf wd (Position i f) = \case
-  Nothing -> fill ' '
-  Just ch -> withAttr columnHdrAttr $ padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str ch)
-
-renderers :: LibraryRenderers
-renderers = GridRenderers {
-  drawCell = \lf wd gc song mc -> let
-    attrFn = if gc ^. #row % #selected && gc ^. #col % #selected 
-      then withAttr colSelectedAttr
-      else id
-    in attrFn $ case mc of
-      Nothing -> fill ' '
-      Just (StringCell s) -> padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str s)
-      Just (TimeCell time) -> let
-        (min, sec) = time `divMod` 60
-        time' = case min of
-          0 -> show sec
-          _ -> show min <> ":" <> show sec
-        in padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max $ str time'
-, drawRowHdr = Just drh
-, drawColHdr = Just dch
--- This is the same as Nothing.
-, drawColHdrRowHdr = Just $ \_ _ -> fill ' '
-}
-
-theList :: LibraryList
-theList = gridTabularList TheList songs 1 sizes contents
-
-runMain :: [String] -> LibraryEventHandler -> IO ()
-runMain msgs eh = do
-  let appState = AppState {
-        libList = theList
-      , libRenderers = renderers
-      , listWidth = 39
-      }
-  void $ defaultMain (getApp msgs eh) appState
diff --git a/demos/Internal/MixedTabularList.hs b/demos/Internal/MixedTabularList.hs
deleted file mode 100644
--- a/demos/Internal/MixedTabularList.hs
+++ /dev/null
@@ -1,176 +0,0 @@
-{-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedLabels #-}
-module Internal.MixedTabularList (
-  runMain
-)
-where
-
-import Brick.Widgets.TabularList.Mixed hiding (sizes, contents)
--- base
-import GHC.Generics (Generic)
-import Control.Monad (void)
--- Third party libraries
-import Optics.Core
-import Data.Sequence (Seq(..))
-import qualified Data.Sequence as S
--- brick
-import Brick.Main
-import Brick.AttrMap
-import Brick.Types
-import Brick.Widgets.Core
-import Brick.Widgets.Center
-import Brick.Widgets.Border
-import Brick.Util
-import Brick.Widgets.List
-import Brick.Widgets.Border.Style
-import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)
-
-data Song = Song {
-  artist :: String
-, title :: String
-, album :: String
-, time :: Int
-} deriving Generic
-
-data LibraryEntry = LibFolder String | LibSong Song deriving Generic
-
-data LibCell = StringCell String | FolderCell String | TimeCell Int deriving Generic
-
-data Name = TheList deriving (Eq, Ord, Show)
-
-type LibraryList = MixedTabularList Name LibraryEntry LibCell Int String
-type LibraryRenderers = MixedRenderers Name LibraryEntry LibCell Int String
-type LibraryContents = MixedContents LibraryEntry LibCell Int String
-type LibraryEventHandler = Event -> EventM Name LibraryList ()
-
-libraryEntries :: Seq LibraryEntry
-libraryEntries = let
-  folders = [LibFolder "[.]", LibFolder "[..]"]
-  songs = map (\n -> LibSong (Song ("Artist " <> show n) ("Title " <> show n) ("Album " <> show n) n)) [1..1000]
-  in S.fromList $ folders ++ songs
-
-contents :: LibraryContents
-contents = let
-  getCell (LibFolder s) 0 = Just $ FolderCell $ "Folder: " <> s
-  getCell (LibFolder _) _ = Nothing
-  getCell (LibSong s) 0 = Just $ StringCell $ s ^. #artist
-  getCell (LibSong s) 1 = Just $ StringCell $ s ^. #title
-  getCell (LibSong s) 2 = Just $ StringCell $ s ^. #album
-  getCell (LibSong s) 3 = Just $ TimeCell $ s ^. #time
-  getCell (LibSong _) _ = Nothing
-  getColumnHeader 0 = Just "Artist"
-  getColumnHeader 1 = Just "Title"
-  getColumnHeader 2 = Just "Album"
-  getColumnHeader 3 = Just "Time"
-  getColumnHeader _ = Nothing
-  getRowHeader _ n = Just (n+1)
-  in MixedContents {
-    cell = getCell
-  , rowHdr = Just getRowHeader
-  , colHdr = Just getColumnHeader
-  }
-
-data AppState = AppState {
-  libList :: LibraryList
-, libRenderers :: LibraryRenderers
-, listWidth :: Int
-} deriving Generic
-
-handleLibEvent :: LibraryEventHandler -> BrickEvent Name () -> EventM Name AppState ()
-handleLibEvent eh e = case e of
-  VtyEvent (EvKey KEsc []) -> halt
-  VtyEvent (EvKey (KChar 'q') []) -> halt
-  VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #drawColHdr) $ modify $ \case
-    Nothing -> Just dch
-    Just _ -> Nothing
-  VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #drawRowHdr) $ modify $ \case
-    Nothing -> Just drh
-    Just _ -> Nothing
-  VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1
-  VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)
-  VtyEvent e -> zoom #libList $ eh e
-  _ -> return ()
-  where abc = []
-
-drawUi :: [String] -> AppState -> [Widget Name]
-drawUi msgs s = let
-  theList = renderMixedTabularList (s ^. #libRenderers) True (s ^. #libList)
-  in [vCenter $ vBox $ hCenter (border $ vLimit 15 $ hLimit (s ^. #listWidth) theList)
-                     : hCenter (str "Press Esc or q to exit" )
-                     : hCenter (str "Press c to toggle column headers")
-                     : hCenter (str "Press r to toggle row headaers")
-                     : hCenter (str "Press - to shorten the list and = to widen the list")
-                     : hCenter (str " ")
-                     : map (hCenter . str) msgs ]
-
-columnHdrAttr :: AttrName
-columnHdrAttr = attrName "columnHeader"
-rowHdrAttr :: AttrName
-rowHdrAttr = attrName "rowHeader"
-
-getApp :: [String] -> LibraryEventHandler -> App AppState () Name
-getApp msgs eh = App {
-  appDraw = drawUi msgs
-, appChooseCursor = neverShowCursor
-, appHandleEvent = handleLibEvent eh
-, appStartEvent = return ()
-, appAttrMap = const $ attrMap defAttr [ (listSelectedAttr, black `on` white)
-                                       , (columnHdrAttr, fg blue)
-                                       , (rowHdrAttr, fg red)]
-}
-
-dch lf ci = \case
-  Nothing -> hCenter $ str " "
-  Just ch -> withAttr columnHdrAttr $ padRight Max (str ch) <+> str " "
-
-drh lf wd (Position i f) row = \case
-  Nothing -> hCenter $ str " "
-  Just rh ->
-    let attrFn = if f
-          then id
-          else withAttr rowHdrAttr
-    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)
-
-renderers :: LibraryRenderers
-renderers = MixedRenderers {
-  drawCell = \lf (Position i f) row mc -> case mc of
-    Nothing -> hCenter $ str " "
-    Just (StringCell s) -> padRight Max (str s) <+> str " "
-    Just (FolderCell s) -> padRight Max $ str s
-    Just (TimeCell time) -> let
-      (min, sec) = time `divMod` 60
-      time' = case min of
-        0 -> show sec
-        _ -> show min <> ":" <> show sec
-      in padRight Max (str time') <+> str " "
-, drawRowHdr = Just drh
-, drawColHdr = Just dch
--- This is the same as Nothing.
-, drawColHdrRowHdr = Just $ \_ _ -> fill ' '
-}
-
-runMain :: [String] -> LibraryEventHandler -> IO ()
-runMain msgs eh = do
-  let sizes = MixedSizes {
-        rowHdr = Just $ VisibleRowHeaders $ \_ rowHs -> (+2) $ maximum $ map (length . show) rowHs
-      , colSizes = AvailWidth $ \aW -> let
-          artist = max 7 $ (aW * 30) `div` 100
-          title = max 6 $ (aW * 30) `div` 100
-          time = 7
-          album = aW - artist - title - time
-          songWidths = [artist, title, album, time]
-          in ColSizes {
-            rowKind = \case
-              LibFolder _ -> [aW]
-              LibSong _ -> songWidths
-          , colHdr = Just (songWidths, 1)
-          }
-      }
-      appState = AppState {
-        libList = mixedTabularList TheList libraryEntries 1 sizes contents
-      , libRenderers = renderers
-      , listWidth = 80
-      }
-  void $ defaultMain (getApp msgs eh) appState
diff --git a/demos/MixedTabularList.hs b/demos/MixedTabularList.hs
deleted file mode 100644
--- a/demos/MixedTabularList.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module Main where
-
-import Internal.MixedTabularList
-import Brick.Widgets.TabularList.Mixed
-
-main :: IO ()
-main = do
-  let msgs = [ "Press Up arrow to go up one item"
-             , "Press Down arrow to go down one item"
-             , "Press PageUp to go up one page"
-             , "Press PageDown to go down one page"
-             , "Press Home to go to the beginning"
-             , "Press End to go to the end" ]
-  runMain msgs handleMixedListEvent
diff --git a/demos/MixedTabularListVi.hs b/demos/MixedTabularListVi.hs
deleted file mode 100644
--- a/demos/MixedTabularListVi.hs
+++ /dev/null
@@ -1,18 +0,0 @@
-module Main where
-
-import Internal.MixedTabularList
-import Brick.Widgets.TabularList.Mixed
-
-main :: IO ()
-main = do
-  let msgs = [ "Press Up arrow or k to go up one item"
-             , "Press Down arrow or j to go down one item"
-             , "Press PageUp or Ctrl+b to go up one page"
-             , "Press PageDown or Ctrl+f to go down one page"
-             , "Press Home or g to go to the beginning"
-             , "Press End or G to go to the end"
-             , "Press Ctrl+u to go up half page"
-             , "Press Ctrl+d to go down half page" ]
-  runMain msgs $ \e -> do
-    handleMixedListEvent e
-    handleMixedListEventVi e
diff --git a/exec/GridTabularList.hs b/exec/GridTabularList.hs
new file mode 100644
--- /dev/null
+++ b/exec/GridTabularList.hs
@@ -0,0 +1,21 @@
+module Main where
+
+import Internal.GridTabularList
+import Brick.Widgets.TabularList.Grid
+
+main :: IO ()
+main = do
+  let msgs = [ "Press Up arrow to go up one item"
+             , "Press Down arrow to go down one item"
+             , "Press PageUp to go up one page"
+             , "Press PageDown to go down one page"
+             , "Press Home to go to the beginning"
+             , "Press End to go to the end" 
+             , " "
+             , "Press Left arrow to go left by one column"
+             , "Press Right arrow to go right by one column"
+             , "Press Ctrl+Home to go to the first column"
+             , "Press Ctrl+End to go to the last column"
+             , "Press Ctrl+PageUp to go left one page of columns"
+             , "Press Ctrl+PageDown to go right one page of columns" ]
+  runMain msgs handleGridListEvent
diff --git a/exec/GridTabularListVi.hs b/exec/GridTabularListVi.hs
new file mode 100644
--- /dev/null
+++ b/exec/GridTabularListVi.hs
@@ -0,0 +1,25 @@
+module Main where
+
+import Internal.GridTabularList
+import Brick.Widgets.TabularList.Grid
+
+main :: IO ()
+main = do
+  let msgs = [ "Press Up arrow or k to go up one item"
+             , "Press Down arrow or j to go down one item"
+             , "Press PageUp or Ctrl+f to go up one page"
+             , "Press PageDown or Ctrl+b to go down one page"
+             , "Press Home or g to go to the beginning"
+             , "Press End or G to go to the end" 
+             , "Press Ctrl+u to go up half page"
+             , "Press Ctrl+d to go down half page"
+             , " "
+             , "Press Left arrow or h to go left by one column"
+             , "Press Right arrow or l to go right by one column"
+             , "Press Ctrl+Home or H to go to the first column"
+             , "Press Ctrl+End or L to go to the last column"
+             , "Press Ctrl+PageUp or Alt+h or Backspace to go left by one page of columns"
+             , "Press Ctrl+PageDown or Alt+l to go right by one page of columns" ]
+  runMain msgs $ \r e -> do
+    handleGridListEvent r e
+    handleGridListEventVi r e
diff --git a/exec/MixedTabularList.hs b/exec/MixedTabularList.hs
new file mode 100644
--- /dev/null
+++ b/exec/MixedTabularList.hs
@@ -0,0 +1,14 @@
+module Main where
+
+import Internal.MixedTabularList
+import Brick.Widgets.TabularList.Mixed
+
+main :: IO ()
+main = do
+  let msgs = [ "Press Up arrow to go up one item"
+             , "Press Down arrow to go down one item"
+             , "Press PageUp to go up one page"
+             , "Press PageDown to go down one page"
+             , "Press Home to go to the beginning"
+             , "Press End to go to the end" ]
+  runMain msgs handleMixedListEvent
diff --git a/exec/MixedTabularListVi.hs b/exec/MixedTabularListVi.hs
new file mode 100644
--- /dev/null
+++ b/exec/MixedTabularListVi.hs
@@ -0,0 +1,18 @@
+module Main where
+
+import Internal.MixedTabularList
+import Brick.Widgets.TabularList.Mixed
+
+main :: IO ()
+main = do
+  let msgs = [ "Press Up arrow or k to go up one item"
+             , "Press Down arrow or j to go down one item"
+             , "Press PageUp or Ctrl+b to go up one page"
+             , "Press PageDown or Ctrl+f to go down one page"
+             , "Press Home or g to go to the beginning"
+             , "Press End or G to go to the end"
+             , "Press Ctrl+u to go up half page"
+             , "Press Ctrl+d to go down half page" ]
+  runMain msgs $ \e -> do
+    handleMixedListEvent e
+    handleMixedListEventVi e
