diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Revision history for brick-tabular-list
 
+## 1.0.0.0 -- 2023-03-10
+
+* Substantially simplified and improved API. It is far less likely to make mistakes with the new API.
+* Substantially improved documentation
+* Removed unneccesary demo programs
+* Inserted a separator below column headers in demo programs
+* Specify 120 characters per line as maximum line length
+* Added `NoFieldSelectors` to modules
+
 ## 0.2.0.1 -- 2023-02-27
 
 * Fixed demo programs for haskell-language-server
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.1
+version: 1.0.0.0
 
 synopsis: Tabular list widgets for brick.
 
@@ -10,6 +10,12 @@
     * Grid tabular list
     * Mixed tabular list
 
+    A tabular list consists of cells(row columns), column headers, and row headers. Column headers and row headers are
+    optional.
+
+    It can handle a very large data set if you delete invisible rows from memory and fetch visible rows from a database
+    (file). For example, SQLite database file can handle a large spreadsheet.
+
     == To get started
 
     * Read "Brick.Widgets.TabularList.Grid" or "Brick.Widgets.TabularList.Mixed".
@@ -21,6 +27,10 @@
 
     For zoom, you have to use van Laarhoven lens because brick supports zoom through microlens.
 
+    == For Contributors
+
+    This library tries not to exceed 120 characters per line.
+
 homepage: https://codeberg.org/amano.kenji/brick-tabular-list
 bug-reports: https://codeberg.org/amano.kenji/brick-tabular-list/issues
 
@@ -45,13 +55,13 @@
 
 library
     build-depends:
-        base >=4.15.1.0 && <5
+        base >=4.16.4.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
+      , containers >=0.6.4 && <0.7
+      , generic-lens >=2.2.1 && <2.3
+      , microlens >=0.4.13 && <0.5
+      , optics-core >=0.4.1 && <0.5
+      , vty >=5.38 && <5.39
     exposed-modules:
         Brick.Widgets.TabularList
       , Brick.Widgets.TabularList.Grid
@@ -63,7 +73,7 @@
     hs-source-dirs: src
     default-language: Haskell2010
 
-library demo
+common demo
     if !flag(demo)
       buildable: False
     build-depends:
@@ -72,36 +82,16 @@
       , brick-tabular-list
       , containers
       , optics-core
+      , vector >=0.12.3.1 && <0.14
       , 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: exec
+    import: demo
     main-is: MixedTabularList.hs
 
-executable mixed-tabular-list-vi
-    import: exec
-    main-is: MixedTabularListVi.hs
-
 executable grid-tabular-list
-    import: exec
+    import: demo
     main-is: GridTabularList.hs
-
-executable grid-tabular-list-vi
-    import: exec
-    main-is: GridTabularListVi.hs
diff --git a/demo/GridTabularList.hs b/demo/GridTabularList.hs
new file mode 100644
--- /dev/null
+++ b/demo/GridTabularList.hs
@@ -0,0 +1,182 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE NoFieldSelectors #-}
+module Main where
+
+import Brick.Widgets.TabularList.Grid
+-- 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
+import qualified Data.Vector as V
+import Data.Vector (Vector)
+-- 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 Name = TheList deriving (Eq, Ord, Show)
+
+data AppState = AppState {
+  libList :: LibraryList
+, libRenderers :: LibraryRenderers
+, listWidth :: Int
+} deriving Generic
+
+type LibraryList = GridTabularList Name Song
+type LibraryRenderers = GridRenderers Name Song Int
+
+songs :: Seq Song
+songs = S.fromList $
+  map (\n -> Song {
+        artist = "Artist " <> show n
+      , title = "Title " <> show n
+      , album = "Album " <> show n
+      , composer = "Composer " <> show n
+      , genre = "Genre " <> show n
+      , time = n
+      })
+      [1..1000]
+
+handleLibEvent :: BrickEvent Name () -> EventM Name AppState ()
+handleLibEvent e = do
+  s <- get
+  let r = s ^. #libRenderers
+  case e of
+    VtyEvent (EvKey KEsc []) -> halt
+    VtyEvent (EvKey (KChar 'q') []) -> halt
+    VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #rowHdr) $ modify $ \case
+      Nothing -> Just rowHdr
+      Just _ -> Nothing
+    VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #colHdr) $ modify $ \case
+      Nothing -> Just colHdr
+      Just _ -> Nothing
+    VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1
+    VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)
+    VtyEvent e -> zoom #libList $ do
+      handleGridListEvent r e
+      handleGridListEventVi r e
+    _ -> return ()
+
+drawUi :: AppState -> [Widget Name]
+drawUi s = 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" ]
+  theList = renderGridTabularList (s ^. #libRenderers) True (s ^. #libList)
+  in [vCenter $ vBox $ hCenter (padLeftRight 2 $ joinBorders $ 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"
+
+colHdrs :: Vector String
+colHdrs = V.fromList ["Artist", "Title", "Album", "Composer", "Genre", "Time"]
+
+rowHdr :: RowHdr Name Song Int
+rowHdr = RowHdr {
+  draw = \_ wd f 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)
+, width = \_ rh -> (+2) $ maximum $ map (length . show) rh
+, toRowHdr = \_ i -> i+1
+}
+
+colHdr :: GridColHdr Name
+colHdr = GridColHdr {
+  draw = \_ wd (FlatContext i _) -> let
+    renderColH = withAttr columnHdrAttr . padRight (Pad $ if wd > 0 then 0 else 1) . padRight Max . str
+    in case colHdrs V.!? i of
+      Just ch -> renderColH ch <=> hBorder
+      Nothing -> emptyWidget
+, height = 2
+}
+
+renderers :: LibraryRenderers
+renderers = GridRenderers {
+  drawCell = \_ wd (GridContext (FlatContext _ rs) (FlatContext ci cs)) s -> let
+    attrFn = if rs && cs
+      then withAttr colSelectedAttr
+      else id
+    renderCell s = padRight (Pad $ if wd > 0 then 0 else 1) (padRight Max $ str s)
+    in attrFn $ case ci of
+      0 -> renderCell $ s ^. #artist
+      1 -> renderCell $ s ^. #title
+      2 -> renderCell $ s ^. #album
+      3 -> renderCell $ s ^. #composer
+      4 -> renderCell $ s ^. #genre
+      5 -> let
+        (min, sec) = (s ^. #time) `divMod` 60
+        in renderCell $ case min of
+          0 -> show sec
+          _ -> show min <> ":" <> show sec
+      _ -> fill ' '
+, rowHdr = Just rowHdr
+, colHdr = Just colHdr
+, drawColHdrRowHdr = Just $ \_ _ -> vLimit 1 (fill ' ') <=> hBorder
+}
+
+theList :: LibraryList
+theList = gridTabularList TheList songs 1 $ S.fromList [12, 11, 11, 14, 11, 7]
+
+main :: IO ()
+main = do
+  let appState = AppState {
+        libList = theList
+      , libRenderers = renderers
+      , listWidth = 39
+      }
+      app = App {
+        appDraw = drawUi
+      , appChooseCursor = neverShowCursor
+      , appHandleEvent = handleLibEvent
+      , appStartEvent = return ()
+      , appAttrMap = const $ attrMap defAttr [ (colSelectedAttr, black `on` white)
+                                             , (columnHdrAttr, fg blue)
+                                             , (rowHdrAttr, fg red)]
+      }
+  void $ defaultMain app appState
diff --git a/demo/Internal/GridTabularList.hs b/demo/Internal/GridTabularList.hs
deleted file mode 100644
--- a/demo/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/demo/Internal/MixedTabularList.hs b/demo/Internal/MixedTabularList.hs
deleted file mode 100644
--- a/demo/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/demo/MixedTabularList.hs b/demo/MixedTabularList.hs
new file mode 100644
--- /dev/null
+++ b/demo/MixedTabularList.hs
@@ -0,0 +1,184 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE NoFieldSelectors #-}
+module Main where
+
+import Brick.Widgets.TabularList.Mixed
+-- 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
+import Data.Vector (Vector)
+import qualified Data.Vector as V
+-- 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 Name = TheList deriving (Eq, Ord, Show)
+
+data Widths = Widths {
+  song :: [Width]
+, folder :: [Width]
+} deriving Generic
+
+data AppState = AppState {
+  libList :: LibraryList
+, libRenderers :: LibraryRenderers
+, listWidth :: Int
+} deriving Generic
+
+type LibraryList = MixedTabularList Name LibraryEntry Widths
+type LibraryRenderers = MixedRenderers Name LibraryEntry Widths Int
+
+libraryEntries :: Seq LibraryEntry
+libraryEntries = let
+  folders = [LibFolder "[.]", LibFolder "[..]"]
+  songs = map (\n -> LibSong $ Song {
+                artist = "Artist " <> show n
+              , title = "Title " <> show n
+              , album = "Album " <> show n
+              , time = n
+              })
+              [1..998]
+  in S.fromList $ folders ++ songs
+
+handleLibEvent :: BrickEvent Name () -> EventM Name AppState ()
+handleLibEvent e = case e of
+  VtyEvent (EvKey KEsc []) -> halt
+  VtyEvent (EvKey (KChar 'q') []) -> halt
+  VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #rowHdr) $ modify $ \case
+    Nothing -> Just rowHdr
+    Just _ -> Nothing
+  VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #colHdr) $ modify $ \case
+    Nothing -> Just colHdr
+    Just _ -> Nothing
+  VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1
+  VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)
+  VtyEvent e -> zoom #libList $ do
+    handleMixedListEvent e
+    handleMixedListEventVi e
+  _ -> return ()
+
+drawUi :: AppState -> [Widget Name]
+drawUi s = let
+  theList = renderMixedTabularList (s ^. #libRenderers) True (s ^. #libList)
+  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" ]
+  in [vCenter $ vBox $ hCenter (joinBorders $ 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"
+
+dc :: ListFocused -> MixedContext -> LibraryEntry -> Widget n
+dc lf (MixedContext (FlatContext _ f) ci) e = let
+  renderPlainCell s = padRight Max (str s) <+> str " "
+  in case e of
+    LibFolder f -> case ci of
+      0 -> padRight Max $ str $ "Folder: " <> f
+      _ -> emptyWidget
+    LibSong (Song {artist, title, album, time}) -> case ci of
+      0 -> renderPlainCell artist
+      1 -> renderPlainCell title
+      2 -> renderPlainCell album
+      3 -> renderPlainCell $ let
+        (min, sec) = time `divMod` 60
+        in case min of
+          0 -> show sec
+          _ -> show min <> ":" <> show sec
+      _ -> emptyWidget
+
+colHdrs :: Vector String
+colHdrs = V.fromList ["Artist", "Title", "Album", "Time"]
+
+cWprk :: AvailWidth -> [LibraryEntry] -> Widths
+cWprk aW _ = let
+  artist = max 7 $ (aW * 30) `div` 100
+  title = max 6 $ (aW * 30) `div` 100
+  time = 7
+  album = aW - artist - title - time
+  in Widths { song = [artist, title, album, time], folder = [aW] }
+
+wpr :: Widths -> LibraryEntry -> [Width]
+wpr (Widths {song, folder}) = \case
+  LibSong _ -> song
+  LibFolder _ -> folder
+
+rowHdr :: RowHdr Name LibraryEntry Int
+rowHdr = RowHdr {
+  draw = \lf wd f 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)
+, width = \_ rh -> (+2) $ maximum $ map (length . show) rh
+, toRowHdr = \_ i -> i+1
+}
+
+colHdr :: MixedColHdr Name Widths
+colHdr = MixedColHdr {
+  draw = \_ ci -> case colHdrs V.!? ci of
+    Just ch -> withAttr columnHdrAttr (padRight Max (str ch) <+> str " ") <=> hBorder
+    Nothing -> emptyWidget
+, widths = \Widths {song} -> song
+, height = 2
+}
+
+main :: IO ()
+main = do
+  let appState = AppState {
+        libList = mixedTabularList TheList libraryEntries 1 cWprk wpr
+      , libRenderers = MixedRenderers {
+          drawCell = dc
+        , rowHdr = Just rowHdr
+        , colHdr = Just colHdr
+        , drawColHdrRowHdr = Just $ \_ _ -> vLimit 1 (fill ' ') <=> hBorder
+        }
+      , listWidth = 80
+      }
+      app = App {
+        appDraw = drawUi
+      , appChooseCursor = neverShowCursor
+      , appHandleEvent = handleLibEvent
+      , appStartEvent = return ()
+      , appAttrMap = const $ attrMap defAttr [ (listSelectedAttr, black `on` white)
+                                             , (columnHdrAttr, fg blue)
+                                             , (rowHdrAttr, fg red)]
+      }
+  void $ defaultMain app appState
diff --git a/exec/GridTabularList.hs b/exec/GridTabularList.hs
deleted file mode 100644
--- a/exec/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/exec/GridTabularListVi.hs b/exec/GridTabularListVi.hs
deleted file mode 100644
--- a/exec/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/exec/MixedTabularList.hs b/exec/MixedTabularList.hs
deleted file mode 100644
--- a/exec/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/exec/MixedTabularListVi.hs b/exec/MixedTabularListVi.hs
deleted file mode 100644
--- a/exec/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/grid-tabular-list-01.png b/grid-tabular-list-01.png
Binary files a/grid-tabular-list-01.png and b/grid-tabular-list-01.png differ
diff --git a/grid-tabular-list-02.png b/grid-tabular-list-02.png
Binary files a/grid-tabular-list-02.png and b/grid-tabular-list-02.png differ
diff --git a/grid-tabular-list-03.png b/grid-tabular-list-03.png
Binary files a/grid-tabular-list-03.png and b/grid-tabular-list-03.png differ
diff --git a/mixed-tabular-list.png b/mixed-tabular-list.png
Binary files a/mixed-tabular-list.png and b/mixed-tabular-list.png differ
diff --git a/src/Brick/Widgets/TabularList.hs b/src/Brick/Widgets/TabularList.hs
--- a/src/Brick/Widgets/TabularList.hs
+++ b/src/Brick/Widgets/TabularList.hs
@@ -4,61 +4,58 @@
 import Brick.Widgets.TabularList.Types
 import Brick.Widgets.Center
 import Brick.Widgets.List
+import Brick.Widgets.Core
+import Data.Void
 
--- * #SharedTraitsOfTabularListWidgets# Shared Traits of Tabular List Widgets
---
--- $SharedTraitsOfTabularListWidgets
--- A tabular list consists of cells, column headers, and row headers. Column headers and row headers are optional.
--- 'WidthDeficit' is calculated for renderers of list elements that are shrunk to the available width.
+-- * Type Variables #TypeVariables#
 --
--- Widths for list elements are either fixed or calculated. Heights are always fixed.
+-- $TypeVariables
+-- * @__n__@ is the type of the resource name for the list. This is not for column headers because column headers are
+-- above the list. Read [brick user guide](https://github.com/jtdaugherty/brick/blob/master/docs/guide.rst) for more
+-- details.
+-- * @__e__@ is the type of list elements which are also called list rows in tabular list widgets.
+-- * @__w__@ is the type that contains widths per row kind. mixed-tabular-list demo program shows how to utilize it.
+-- * @__r__@ is the type for row headers. If you don't want to show row headers, set this to @__()__@ or 'Void'.
+
+-- * Rendering #Rendering#
 --
+-- $Rendering
 -- Because tabular list widgets build on top of 'GenericList', attributes for 'GenericList' apply to tabular list
 -- widgets if the attributes are defined in your brick application.
 --
--- It can handle a very large data set if you delete invisible rows from memory and fetch visible rows from a database
--- (file). For example, SQLite database file can handle a large spreadsheet.
-
--- * #Rendering# Rendering
---
--- $Rendering
--- A rendering function for cells or row headers is given a space with
+-- A rendering function for row columns or row headers is given a space with
 --
--- > setAvailableSize (width, listItemHeight)
+-- @
+-- 'setAvailableSize' (width, listItemHeight)
+-- @
 --
--- A rendering function for column headers and column header row header is given a space with
+-- A rendering function for column headers or column header row header is given a space with
 --
--- > setAvailableSize (width, columnHeaderHeight)
+-- @
+-- 'setAvailableSize' (width, columnHeaderHeight)
+-- @
 --
 -- If the given height for rendering functions is not claimed, the list will look broken.
 --
 -- The given width for rendering functions must be claimed by the returned widget with brick's padding functions or
 -- 'hCenter'. Otherwise, the list will look broken.
 --
--- The following examples show how a rendering function can claim the available width.
---
--- > padRight (Pad $ if widthDeficit > 0 then 0 else 1) $ padLeft Max content
+-- 'WidthDeficit' is calculated for renderers of tabular list components that are shrunk to the available width.
 --
--- > padRight Max content <+> str " "
+-- The following examples show how a rendering function can claim the available width.
 --
--- > padLeft (Pad $ if widthDeficit > 0 then 0 else 1) $ hCenter content
+-- @
+-- 'padRight' ('Pad' $ if widthDeficit > 0 then 0 else 1) $ 'padLeft' 'Max' content
+-- @
 --
--- In the examples above, I used padding with one character at the left or the right to introduce gaps between columns.
--- If 'WidthDeficit' is positive, you may want to remove padding because the element that is being rendered is not
--- followed or preceded by other columns.
+-- @
+-- 'padRight' 'Max' content '<+>' 'str' " "
+-- @
 --
--- If row headers and column headers are drawn and the renderer for column header row header(drawColHdrRowHdr) doesn't
--- exist, then column header row header is filled with empty space. The renderer for column header row header merely
--- allows you to customize column header row header.
-
--- * #ListTypeVariables# List Type Variables
+-- @
+-- 'padLeft' ('Pad' $ if widthDeficit > 0 then 0 else 1) $ 'hCenter' content
+-- @
 --
--- $ListTypeVariables
--- * @__n__@ is the type of the resource name for the list. This is not for column headers because column headers are
--- above the list. Read [brick user guide](https://github.com/jtdaugherty/brick/blob/master/docs/guide.rst) for more
--- details.
--- * @__row__@ is the type of each row.
--- * @__cell__@ is the type of columns of each row. If you want to differentiate different columns, @__cell__@ should
--- have a data constructor for each type of column.
--- * @__rowH__@ is the type of row header.
--- * @__colH__@ is the type of column header.
+-- In the examples above, I used padding with one space character at the left or the right to introduce gaps between
+-- columns. If 'WidthDeficit' is positive, you may want to remove padding because a column is not followed or preceded
+-- by other columns.
diff --git a/src/Brick/Widgets/TabularList/Grid.hs b/src/Brick/Widgets/TabularList/Grid.hs
--- a/src/Brick/Widgets/TabularList/Grid.hs
+++ b/src/Brick/Widgets/TabularList/Grid.hs
@@ -1,18 +1,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE NoMonomorphismRestriction #-}
-{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE NoFieldSelectors #-}
+{-# LANGUAGE NamedFieldPuns #-}
 -- | Grid tabular list is a uniform grid that supports cell-by-cell navigation.
 --
--- ![ ](grid-tabular-list-01.png) ![ ](grid-tabular-list-02.png) ![ ](grid-tabular-list-03.png)
---
--- Read [Shared Traits of Tabular List Widgets]("Brick.Widgets.TabularList#g:SharedTraitsOfTabularListWidgets")
--- before reading further.
+-- ![demo-01](grid-tabular-list-01.png) ![demo-02](grid-tabular-list-02.png) ![demo-03](grid-tabular-list-03.png)
 --
--- Because this list is designed to show an arbitrary number of columns, horizontal scrolling is supported through
+-- Because this list is designed to show arbitrary numbers of columns, horizontal scrolling is supported through
 -- cell-by-cell navigation.
 --
 -- Grid tabular list tries to show the current column in the center. If it can't show the current column in the center,
@@ -21,16 +17,16 @@
 -- It should be fast enough to handle a large spreadsheet. It is also suitable for an interface to a database table.
 module Brick.Widgets.TabularList.Grid (
 -- * Data types
-  GridContents(..)
-, GridContext(..)
+  GridContext(..)
+, GridColHdr(..)
 , GridRenderers(..)
-, GridSizes(..)
+, GridWidths
 , GridTabularList(..)
 -- * List construction
 , gridTabularList
 -- * Rendering
 , renderGridTabularList
--- * Column navigation
+-- * Column navigation #ColumnNavigation#
 , gridMoveLeft
 , gridMoveRight
 , gridMoveTo
@@ -53,7 +49,7 @@
 import Data.Foldable (toList)
 import Control.Monad (unless)
 -- Third party libraries
-import Optics.Core hiding (Empty)
+import Optics.Core ( (&), (%), (%~), (.~), (^.) )
 import qualified Data.Sequence as S
 import Data.Sequence (Seq(..))
 import Data.Generics.Labels
@@ -61,62 +57,59 @@
 import qualified Brick.Widgets.List as L
 import Brick.Types
 import Brick.Widgets.Core
-import Brick.Widgets.Center
 import Graphics.Vty (Event(..), Key(..), Modifier(..))
 import Brick.Main (lookupViewport)
 
--- | Functions for getting contents of grid tabular list elements.
--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data GridContents n row cell rowH colH = GridContents {
-  cell :: row -> ColumnIndex -> Maybe cell
-, rowHdr :: Maybe (row -> RowIndex -> Maybe rowH)
-, colHdr :: Maybe (ColumnIndex -> Maybe colH)
-} deriving Generic
-
--- | Context information for grid cells
+-- | Context for grid cells
 data GridContext = GridContext {
-  row :: Position -- ^ Position among rows
-, col :: Position -- ^ Position among columns
+  row :: FlatContext -- ^ Row context
+, col :: FlatContext -- ^ Column context
 } deriving (Show, Generic)
 
--- | Rendering functions for elements of grid tabular list. See
+-- | Grid column header
 --
--- * [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables")
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
 -- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
-data GridRenderers n row cell rowH colH = GridRenderers {
-  drawCell :: ListFocused -> WidthDeficit -> GridContext -> row -> Maybe cell -> Widget n
-, drawRowHdr :: Maybe (ListFocused -> WidthDeficit -> Position -> row -> Maybe rowH -> Widget n)
-, drawColHdr :: Maybe (ListFocused -> WidthDeficit -> Position -> Maybe colH -> Widget n)
-, drawColHdrRowHdr :: Maybe (ListFocused -> WidthDeficit -> Widget n)
+data GridColHdr n = GridColHdr {
+  draw :: ListFocused -> WidthDeficit -> FlatContext -> Widget n
+, height :: Height -- ^ Height for column headers and column header row header
 } deriving Generic
 
--- | Sizes for elements of grid tabular list.
--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data GridSizes rowH = GridSizes {
-  row :: Seq Width -- ^ Widths for column headers and row columns
-, rowHdr :: Maybe (RowHeaderWidth rowH) -- ^ Width for row headers
-, colHdr :: Maybe Height -- ^ Height for column headers
+-- | Rendering functions for components of grid tabular list
+--
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
+data GridRenderers n e r = GridRenderers {
+  drawCell :: ListFocused -> WidthDeficit -> GridContext -> e -> Widget n
+, rowHdr :: Maybe (RowHdr n e r)
+, colHdr :: Maybe (GridColHdr n)
+, drawColHdrRowHdr :: DrawColHdrRowHdr n
 } deriving Generic
 
--- | See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data GridTabularList n row cell rowH colH = GridTabularList {
-  list :: L.GenericList n Seq row -- ^ The underlying primitive list that comes from brick.
-, sizes :: GridSizes rowH
-, contents :: GridContents n row cell rowH colH
-, currentColumn :: ColumnIndex
+-- | Widths for column headers and row columns.
+type GridWidths = Seq Width
+
+-- | * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+data GridTabularList n e = GridTabularList {
+  -- | The underlying primitive list that comes from brick
+  list :: L.GenericList n Seq e
+, widths :: GridWidths
+  -- | Manipulating this field directly is unsafe. Use
+  -- [column navigation]("Brick.Widgets.TabularList.Grid#g:ColumnNavigation") functions to manipulate this. If you still
+  -- want to manipulate this directly, create a function that manipulates it, and test the function properly.
+, currentColumn :: Index
 } deriving Generic
 
 -- | Create a grid tabular list
-gridTabularList :: n -- ^ The list name (must be unique)
-  -> Seq row -- ^ The initial list rows
+gridTabularList
+  :: n -- ^ The list name (must be unique)
+  -> Seq e -- ^ The initial list elements
   -> ListItemHeight
-  -> GridSizes rowH
-  -> GridContents n row cell rowH colH
-  -> GridTabularList n row cell rowH colH
-gridTabularList n rows h sizes contents = GridTabularList {
+  -> GridWidths
+  -> GridTabularList n e
+gridTabularList n rows h widths = GridTabularList {
   list = L.list n rows h
-, sizes = sizes
-, contents = contents
+, widths = widths
 , currentColumn = 0
 }
 
@@ -154,9 +147,9 @@
 -- | Calculate visible columns with the width available for columns. It tries to show the current column in the center.
 -- If it can't show the current column in the center, the first column is shown at the left corner, or the last column
 -- is shown at the right corner.
-visibleColumns :: GridTabularList n row cell rowH colH -> AvailWidth -> VisibleColumns
+visibleColumns :: GridTabularList n e -> AvailWidth -> VisibleColumns
 visibleColumns l aW = let curCol = l ^. #currentColumn in
-  case S.splitAt curCol (l ^. #sizes % #row) of
+  case S.splitAt curCol (l ^. #widths) of
     -- If current column is outside the boundary of row columns, return `NoColumn`.
     (_, Empty) -> NoColumn
     (left, cW :<| right) -> if aW <= 0
@@ -165,7 +158,7 @@
       -- If the available width is less than the current column's width,
       else if cW >= aW
       then CurrentColumn
-      -- Otherwise, calculate the leftmost visible column for the current column shown in the center.
+      -- Otherwise,
       else let
         -- The amount of space to the left of the current column shown in the center.
         lW = (aW - cW) `div` 2
@@ -206,17 +199,19 @@
           else AnchoredRight { left = idx, offset = accW+w-aW, totalWidth = accW+w }
         -- If there aren't enough columns to fill the available width with the last column at the right corner, return
         -- 'AnchoredLeft' with the last column as the rightmost visible column.
-        leftForRight Empty _ _ = AnchoredLeft $ length (l ^. #sizes % #row) - 1
+        leftForRight Empty _ _ = AnchoredLeft $ length (l ^. #widths) - 1
+        -- Calculate the leftmost visible column for the current column shown in the center.
         in leftForMiddle left (curCol-1) 0
 
-renderColumns :: GridTabularList n row cell rowH colH
+renderColumns
+  :: GridTabularList n e
   -> VisibleColumns
-  -> (WidthDeficit -> ColumnIndex -> Width -> Widget n)
+  -> (WidthDeficit -> Index -> Width -> Widget n)
+  -> Height
   -> Widget n
-renderColumns l vCs dC = Widget Greedy Fixed $ do
+renderColumns l vCs dC h = Widget Greedy Fixed $ do
   c <- getContext
-  let cWs = l ^. #sizes % #row
-      iH = l ^. #list % #listItemHeight
+  let cWs = l ^. #widths
       curCol = l ^. #currentColumn
       aW = c^^.availWidthL
   render $ case vCs of
@@ -225,98 +220,93 @@
       Nothing -> error $ "Current column, " <> show curCol <> " is outside the boundary of column widths."
       Just cW -> dC (max 0 $ cW - aW) curCol aW
     AnchoredLeft right -> hBox $ zipWith (dC 0) [0..] $ toList $ S.take (right+1) cWs
-    MiddleColumns {..} -> cropLeftBy offset $ setAvailableSize (totalWidth, iH) $
+    MiddleColumns {left, right, offset, totalWidth} -> cropLeftBy offset $ setAvailableSize (totalWidth, h) $
       hBox $ zipWith (dC 0) [left..] $ toList $ S.take (right-left+1) $ S.drop left cWs
-    AnchoredRight {..} -> cropLeftBy offset $ setAvailableSize (totalWidth, iH) $
+    AnchoredRight {left, offset, totalWidth} -> cropLeftBy offset $ setAvailableSize (totalWidth, h) $
       hBox $ zipWith (dC 0) [left..] $ toList $ S.drop left cWs
 
 -- | Render grid tabular list
 renderGridTabularList :: (Ord n, Show n)
-  => GridRenderers n row cell rowH colH -- ^ Renderers
+  => GridRenderers n e r
   -> ListFocused
-  -> GridTabularList n row cell rowH colH -- ^ The list
+  -> GridTabularList n e -- ^ The list
   -> Widget n
 renderGridTabularList r lf l = Widget Greedy Greedy $ do
   c <- getContext
-  let drawCell = r ^. #drawCell
-      cell = l ^. #contents % #cell
-      list = l ^. #list
-      curCol = l ^. #currentColumn
-      aW = c^^.availWidthL
+  let aW = c^^.availWidthL
       aH = c^^.availHeightL
-      iH = list ^. #listItemHeight
-      wSet = setAvailableSize . (, iH)
-      colHdrRow vCs rhw' rhwd = case (l ^. #contents % #colHdr, l ^. #sizes % #colHdr, r ^. #drawColHdr) of
-        (Nothing, _, _) -> emptyWidget
-        (_, Nothing, _) -> emptyWidget
-        (_, _, Nothing) -> emptyWidget
-        (Just colH, Just colHdrH, Just dch) -> let
-          drawCol wd c w = setAvailableSize (w, colHdrH) $ dch lf wd (Position c (c == curCol)) $ colH c
+      GridRenderers {drawCell} = r
+      GridTabularList {list=l', currentColumn=curCol} = l
+      iH = l' ^. #listItemHeight
+      colHdrRow vCs rhw' rhwd = case r ^. #colHdr of
+        Nothing -> emptyWidget
+        Just (GridColHdr {draw, height}) -> let
+          drawCol wd c w = setAvailableSize (w, height) $ draw lf wd (FlatContext c (c == curCol))
           chrw = case r ^. #drawColHdrRowHdr of
             Nothing -> fill ' '
             Just dchrw -> dchrw lf rhwd
-          in setAvailableSize (rhw', colHdrH) chrw <+> renderColumns l vCs drawCol
+          in setAvailableSize (rhw', height) chrw <+> renderColumns l vCs drawCol height
       renderRow vCs i f r = let
-        drawCol wd c w = let gc = GridContext (Position i f) (Position c $ c == curCol)
-          in wSet w $ drawCell lf wd gc r $ cell r c
-        in renderColumns l vCs drawCol
+        drawCol wd c w = let gc = GridContext (FlatContext i f) (FlatContext c $ c == curCol)
+          in setAvailableSize (w, iH) $ drawCell lf wd gc r
+        in renderColumns l vCs drawCol iH
       renderList = let vCs = visibleColumns l aW in
-        render $ colHdrRow vCs 0 0 <=> L.renderListWithIndex (renderRow vCs) lf list
-      renderHdrList rh rhw drh = let
+        render $ colHdrRow vCs 0 0 <=> L.renderListWithIndex (renderRow vCs) lf l'
+      renderHdrList (RowHdr {draw, width, toRowHdr}) = let
+        rhw = width aW $ zipWithVisibleRowsAndIndexes l' aH toRowHdr
         rhw' = min rhw aW
         rhwd = max 0 $ rhw - aW
         vCs = visibleColumns l $ aW - rhw'
-        renderHdrRow i f row = wSet rhw' (drh lf rhwd (Position i f) row $ rh row i) <+> renderRow vCs i f row
-        in render $ colHdrRow vCs rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf list
-  case (l ^. #contents % #rowHdr, l ^. #sizes % #rowHdr, r ^. #drawRowHdr) of
-    (Nothing, _, _) -> renderList
-    (_, Nothing, _) -> renderList
-    (_, _, Nothing) -> renderList
-    (Just rh, Just (FixedRowHeader w), Just drh) -> renderHdrList rh w drh
-    (Just rh, Just (AvailRowHeader w), Just drh) -> renderHdrList rh (w aW) drh
-    (Just rh, Just (VisibleRowHeaders w), Just drh) -> renderHdrList rh (w aW $ visibleRowHdrs list aH rh) drh
+        renderHdrRow i f r = setAvailableSize (rhw', iH) (draw lf rhwd f $ toRowHdr r i) <+> renderRow vCs i f r
+        in render $ colHdrRow vCs rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf l'
+  maybe renderList renderHdrList $ r ^. #rowHdr
 
 -- | Move to the left by one column.
-gridMoveLeft :: GridTabularList n row cell rowH colH -- ^ The list
-  -> GridTabularList n row cell rowH colH
+gridMoveLeft
+  :: GridTabularList n e -- ^ The list
+  -> GridTabularList n e
 gridMoveLeft gl = if null $ gl ^. #list % #listSelected
   then gl
   else gl & #currentColumn %~ max 0 . subtract 1
 
 -- | Move to the right by one column.
-gridMoveRight :: GridTabularList n row cell rowH colH -- ^ The list
-  -> GridTabularList n row cell rowH colH
+gridMoveRight
+  :: GridTabularList n e -- ^ The list
+  -> GridTabularList n e
 gridMoveRight gl = if null $ gl ^. #list % #listSelected
   then gl
-  else gl & #currentColumn %~ min (length (gl ^. #sizes % #row) - 1) . (+1)
+  else gl & #currentColumn %~ min (length (gl ^. #widths) - 1) . (+1)
 
 -- | Move to the given column index
-gridMoveTo :: ColumnIndex
-  -> GridTabularList n row cell rowH colH -- ^ The list
-  -> GridTabularList n row cell rowH colH
+gridMoveTo
+  :: Index
+  -> GridTabularList n e -- ^ The list
+  -> GridTabularList n e
 gridMoveTo n gl = if null $ gl ^. #list % #listSelected
   then gl
-  else gl & #currentColumn .~ (max 0 . min (length (gl ^. #sizes % #row) - 1)) n
+  else gl & #currentColumn .~ (max 0 . min (length (gl ^. #widths) - 1)) n
 
 -- | Move to the first column.
-gridMoveToBeginning :: GridTabularList n row cell rowH colH -- ^ The list
-  -> GridTabularList n row cell rowH colH
+gridMoveToBeginning
+  :: GridTabularList n e -- ^ The list
+  -> GridTabularList n e
 gridMoveToBeginning gl = if null $ gl ^. #list % #listSelected
   then gl
   else gl & #currentColumn .~ 0
 
 -- | Move to the last column.
-gridMoveToEnd :: GridTabularList n row cell rowH colH -- ^ The list
-  -> GridTabularList n row cell rowH colH 
+gridMoveToEnd
+  :: GridTabularList n e -- ^ The list
+  -> GridTabularList n e
 gridMoveToEnd gl = if null $ gl ^. #list % #listSelected
   then gl
-  else gl & #currentColumn .~ length (gl ^. #sizes % #row) - 1
+  else gl & #currentColumn .~ length (gl ^. #widths) - 1
 
 -- | 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.
 gridMovePage :: Ord n
-  => GridRenderers n row cell rowH colH
-  -> (VisibleColumns -> EventM n (GridTabularList n row cell rowH colH) ())
-  -> EventM n (GridTabularList n row cell rowH colH) ()
+  => GridRenderers n e r
+  -> (VisibleColumns -> EventM n (GridTabularList n e) ())
+  -> EventM n (GridTabularList n e) ()
 gridMovePage r f = do
   l <- get
   unless (null $ l ^. #list % #listSelected) $ do
@@ -325,39 +315,35 @@
       Nothing -> return ()
       Just vp -> let
         (aW, aH) = vp ^. #_vpSize
-        rhw = case (l ^. #contents % #rowHdr, l ^. #sizes % #rowHdr, r ^. #drawRowHdr) of
-          (Nothing, _, _) -> 0
-          (_, Nothing, _) -> 0
-          (_, _, Nothing) -> 0
-          (_, Just (FixedRowHeader w), _) -> w
-          (_, Just (AvailRowHeader w), _) -> w aW
-          (Just rowH, Just (VisibleRowHeaders w), _) -> w aW $ visibleRowHdrs (l ^. #list) aH rowH
+        rhw = case r ^. #rowHdr of
+          Nothing -> 0
+          Just (RowHdr {width, toRowHdr}) -> width aW $ zipWithVisibleRowsAndIndexes (l ^. #list) aH toRowHdr
         in f $ visibleColumns l $ aW - rhw
 
 -- | Move to the previous page of columns.
 --
--- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.
+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected.
 gridMovePageUp :: Ord n
-  => GridRenderers n row cell rowH colH -- ^ Renderers
-  -> EventM n (GridTabularList n row cell rowH colH) ()
+  => GridRenderers n e r -- ^ Renderers
+  -> EventM n (GridTabularList n e) ()
 gridMovePageUp r = gridMovePage r $ \case
   NoColumn -> return ()
   CurrentColumn -> modify gridMoveLeft
-  AnchoredLeft {} -> modify gridMoveToBeginning
-  MiddleColumns {..} -> modify $ gridMoveTo left
-  AnchoredRight {..} -> modify $ gridMoveTo left
+  AnchoredLeft _ -> modify gridMoveToBeginning
+  MiddleColumns {left} -> modify $ gridMoveTo left
+  AnchoredRight {left} -> modify $ gridMoveTo left
 
 -- | Move to the next page of columns.
 --
--- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.
+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected.
 gridMovePageDown :: Ord n
-  => GridRenderers n row cell rowH colH -- ^ Renderers
-  -> EventM n (GridTabularList n row cell rowH colH) ()
+  => GridRenderers n e r -- ^ Renderers
+  -> EventM n (GridTabularList n e) ()
 gridMovePageDown r = gridMovePage r $ \case
   NoColumn -> return ()
   CurrentColumn -> modify gridMoveRight
-  AnchoredLeft {..} -> modify $ gridMoveTo right
-  MiddleColumns {..} -> modify $ gridMoveTo right
+  AnchoredLeft {right} -> modify $ gridMoveTo right
+  MiddleColumns {right} -> modify $ gridMoveTo right
   AnchoredRight {} -> modify gridMoveToEnd
 
 -- | Handle events for grid tabular list with navigation keys.
@@ -371,10 +357,10 @@
 -- * Move to the previous page of columns (Ctrl+PageUp)
 -- * Move to the next page of columns (Ctrl+PageDown)
 --
--- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.
+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected.
 handleGridListEvent :: Ord n
-  => GridRenderers n row cell rowH colH -- ^ Renderers
-  -> Event -> EventM n (GridTabularList n row cell rowH colH) ()
+  => GridRenderers n e r -- ^ Renderers
+  -> Event -> EventM n (GridTabularList n e) ()
 handleGridListEvent r e = case e of
   EvKey KLeft [] -> modify gridMoveLeft
   EvKey KRight [] -> modify gridMoveRight
@@ -395,10 +381,10 @@
 -- * Move to the previous page of columns (Alt+h)
 -- * Move to the next page of columns (Alt+l)
 --
--- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.
+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected.
 handleGridListEventVi :: Ord n
-  => GridRenderers n row cell rowH colH -- ^ Renderers
-  -> Event -> EventM n (GridTabularList n row cell rowH colH) ()
+  => GridRenderers n e r -- ^ Renderers
+  -> Event -> EventM n (GridTabularList n e) ()
 handleGridListEventVi r e = case e of
   EvKey (KChar 'h') [] -> modify gridMoveLeft
   EvKey (KChar 'l') [] -> modify gridMoveRight
diff --git a/src/Brick/Widgets/TabularList/Internal/Common.hs b/src/Brick/Widgets/TabularList/Internal/Common.hs
--- a/src/Brick/Widgets/TabularList/Internal/Common.hs
+++ b/src/Brick/Widgets/TabularList/Internal/Common.hs
@@ -1,7 +1,8 @@
 module Brick.Widgets.TabularList.Internal.Common (
-  visibleRows
-, visibleRowHdrs
-, visibleRowsAndRowHdrs
+  AvailHeight
+, visibleRowsWithStart
+, visibleRows
+, zipWithVisibleRowsAndIndexes
 ) where
 
 import Brick.Widgets.TabularList.Types
@@ -14,9 +15,11 @@
 -- Brick
 import Brick.Widgets.List
 
-type Start = Int
+-- | Height available for 'GenericList'
 type AvailHeight = Int
-visibleRowsWithStart :: GenericList n Seq row -> AvailHeight -> ([row], Start)
+
+-- | Return visible rows and the index of the first visible row, given 'GenericList' and height available for list.
+visibleRowsWithStart :: GenericList n Seq row -> AvailHeight -> ([row], Index)
 visibleRowsWithStart l aH = let
   idx = fromMaybe 0 (l^.listSelectedL)
   numPerHeight = case aH `divMod` (l^.listItemHeightL) of
@@ -27,16 +30,11 @@
   rows = toList $ slice start length $ l^.listElementsL
   in (rows, start)
 
--- | Return visible rows, given 'GenericList' and height available for list elements
-visibleRows :: GenericList n Seq row -> AvailHeight -> [row]
-visibleRows l aH = visibleRowsWithStart l aH ^. _1
-
--- | Return visible row headers, given 'GenericList' and height available for list elements
-visibleRowHdrs :: GenericList n Seq row -> AvailHeight -> (row -> RowIndex -> Maybe rowH) -> [rowH]
-visibleRowHdrs l aH rowH = visibleRowsAndRowHdrs l aH rowH ^. _2
+-- | Return visible rows, given 'GenericList' and height available for list.
+visibleRows :: GenericList n Seq e -> AvailHeight -> [e]
+visibleRows l aH = fst $ visibleRowsWithStart l aH
 
--- | Return visible rows and visible row headers, given 'GenericList', available height, and row header function.
-visibleRowsAndRowHdrs :: GenericList n Seq row -> AvailHeight -> (row -> RowIndex -> Maybe rowH) -> ([row], [rowH])
-visibleRowsAndRowHdrs l aH rowH = let
-  (rows, start) = visibleRowsWithStart l aH
-  in (rows, catMaybes $ zipWith rowH rows [start..])
+-- | Zip visible rows and their row indexes with a function.
+zipWithVisibleRowsAndIndexes :: GenericList n Seq e -> AvailHeight -> (e -> Index -> c) -> [c]
+zipWithVisibleRowsAndIndexes l aH f = let (es, s) = visibleRowsWithStart l aH
+  in zipWith f es [s..]
diff --git a/src/Brick/Widgets/TabularList/Mixed.hs b/src/Brick/Widgets/TabularList/Mixed.hs
--- a/src/Brick/Widgets/TabularList/Mixed.hs
+++ b/src/Brick/Widgets/TabularList/Mixed.hs
@@ -1,15 +1,11 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE BlockArguments #-}
-{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE NoFieldSelectors #-}
 -- | Mixed tabular list is a list with different kinds of rows.
 --
--- ![ ](mixed-tabular-list.png)
---
--- Read [Shared Traits of Tabular List Widgets]("Brick.Widgets.TabularList#g:SharedTraitsOfTabularListWidgets")
--- before reading further.
+-- ![demo](mixed-tabular-list.png)
 --
 -- Each row belongs to a row kind which is usually a data constructor of row data type. Because there can be more than
 -- one data constructor in row data type, this list is called mixed tabular list. Each row kind can have a different
@@ -20,11 +16,11 @@
 -- Because this list is designed to show every column in the available space, horizontal scrolling is not supported.
 module Brick.Widgets.TabularList.Mixed (
 -- * Data types
-  MixedContents(..)
+  MixedContext(..)
+, MixedColHdr(..)
 , MixedRenderers(..)
-, ColSizes(..)
-, CalcColSizes(..)
-, MixedSizes(..)
+, CalcWidthsPerRowKind
+, WidthsPerRow
 , MixedTabularList(..)
 -- * List construction
 , mixedTabularList
@@ -46,140 +42,118 @@
 -- Third party libraries
 import Data.Map.Strict (Map)
 import qualified Data.Map.Strict as Map
-import Optics.Core
+import Optics.Core ( (^.) )
 import Data.Generics.Labels
 import Data.Sequence (Seq)
 -- Brick & Vty
 import qualified Brick.Widgets.List as L
 import Brick.Types
 import Brick.Widgets.Core
-import Brick.Widgets.Center
 import Graphics.Vty.Input.Events (Event)
 
--- | Functions for getting contents of mixed tabular list elements.
--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data MixedContents row cell rowH colH = MixedContents {
-  cell :: row -> ColumnIndex -> Maybe cell -- ^ Function for obtaining a column of a row
-, rowHdr :: Maybe (row -> RowIndex -> Maybe rowH) -- ^ Function for getting row header
-, colHdr :: Maybe (ColumnIndex -> Maybe colH) -- ^ Function for getting column header
-} deriving Generic
+-- | Context for mixed columns
+data MixedContext = MixedContext {
+  row :: FlatContext -- ^ Row context
+, col :: Index -- ^ Index of column
+}
 
--- | Rendering functions for elements of mixed tabular list. See
+-- | Mixed column header
 --
--- * [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables")
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
 -- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
-data MixedRenderers n row cell rowH colH = MixedRenderers {
-  drawCell :: ListFocused -> Position -> row -> Maybe cell -> Widget n
-, drawRowHdr :: Maybe (ListFocused -> WidthDeficit -> Position -> row -> Maybe rowH -> Widget n)
-, drawColHdr :: Maybe (ListFocused -> ColumnIndex -> Maybe colH -> Widget n)
-, drawColHdrRowHdr :: Maybe (ListFocused -> WidthDeficit -> Widget n)
+data MixedColHdr n w = MixedColHdr {
+  draw :: ListFocused -> Index -> Widget n
+  -- | A function for getting widths for column headers from the type that contains widths per row kind
+, widths :: w -> [Width]
+, height:: Height -- ^ Height for column headers and column header row header
 } deriving Generic
 
--- | Column sizes calculated after row header width is calculated.
--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data ColSizes row = ColSizes {
-  -- | Widths for each row kind. Use pattern matching to detect the kind of each row. Usually, a row kind is a data
-  -- constructor of row. The returned widths should be pre-calculated widths. If widths are calculated in this function,
-  -- width calculation will be repeated at every row. If the function is given pre-calculated widths, width calculation
-  -- is done only once.
-  rowKind :: row -> [Width]
-  -- | Widths and height for column headers. Widths and height don't have to be pre-calculated because column headers are
-  -- only drawn once. Because they are drawn once, any calculation done in this field is also done only once.
-, colHdr :: Maybe ([Width], Height)
+-- | Rendering functions for components of mixed tabular list
+--
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
+data MixedRenderers n e w r = MixedRenderers {
+  drawCell :: ListFocused -> MixedContext -> e -> Widget n
+, rowHdr :: Maybe (RowHdr n e r)
+, colHdr :: Maybe (MixedColHdr n w)
+, drawColHdrRowHdr :: DrawColHdrRowHdr n
 } deriving Generic
 
--- | Function for calculating column sizes.
--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data CalcColSizes row =
-  -- | Calculate sizes with the width available after row header width is calculated.
-  AvailWidth (AvailWidth -> ColSizes row)
-  -- | Calculate sizes with visible rows and the width available after row header width is calculated.
-  | VisibleRows ([row] -> AvailWidth -> ColSizes row) deriving Generic
+-- | Calculate widths per row kind from visible list rows and the width available after row header.
+--
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+type CalcWidthsPerRowKind e w = AvailWidth -> [e] -> w
 
--- | Sizes for elements of mixed tabular list.
--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data MixedSizes row rowH = MixedSizes {
-  rowHdr :: Maybe (RowHeaderWidth rowH) -- ^ Width for row headers
-, colSizes :: CalcColSizes row -- ^ Function for calculating column sizes after row header width is calculated
-} deriving Generic
+-- | It is a function to get widths for each row. Use pattern matching to detect the kind of each row. Usually, a row
+-- kind is a data constructor of row data type.
+--
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+type WidthsPerRow e w = w -> e -> [Width]
 
--- | See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").
-data MixedTabularList n row cell rowH colH = MixedTabularList {
-  list :: L.GenericList n Seq row -- ^ The underlying primitive list that comes from brick
-, sizes :: MixedSizes row rowH
-, contents :: MixedContents row cell rowH colH
+-- | * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+data MixedTabularList n e w = MixedTabularList {
+  list :: L.GenericList n Seq e -- ^ The underlying list that comes from brick
+, calcWidthsPerRowKind :: CalcWidthsPerRowKind e w
+, widthsPerRow :: WidthsPerRow e w
 } deriving Generic
 
 -- | Create a mixed tabular list.
 mixedTabularList :: n -- ^ The name of the list. It must be unique.
-  -> Seq row -- ^ The initial list rows
+  -> Seq e -- ^ The initial list elements
   -> ListItemHeight
-  -> MixedSizes row rowH
-  -> MixedContents row cell rowH colH
-  -> MixedTabularList n row cell rowH colH
-mixedTabularList n rows h sizes contents = MixedTabularList {
+  -> CalcWidthsPerRowKind e w
+  -> WidthsPerRow e w
+  -> MixedTabularList n e w
+mixedTabularList n rows h cWprk wpr = MixedTabularList {
   list = L.list n rows h
-, sizes = sizes
-, contents = contents
+, calcWidthsPerRowKind = cWprk
+, widthsPerRow = wpr
 }
 
--- | Render mixed tabular list
+-- | Render mixed tabular list.
 renderMixedTabularList :: (Show n, Ord n)
-  => MixedRenderers n row cell rowH colH -- ^ Renderers
+  => MixedRenderers n e w r -- ^ Renderers
   -> ListFocused
-  -> MixedTabularList n row cell rowH colH -- ^ The list
+  -> MixedTabularList n e w -- ^ The list
   -> Widget n
 renderMixedTabularList r lf l = Widget Greedy Greedy $ do
   c <- getContext
-  let drawCell = r ^. #drawCell
-      cell = l ^. #contents % #cell
-      aW = c^^.availWidthL
+  let aW = c^^.availWidthL
       aH = c^^.availHeightL
-      iH = l ^. #list % #listItemHeight
-      wSet = setAvailableSize . (, iH)
-      colHdrRow sizes rhw' rhwd = case (l ^. #contents % #colHdr, sizes, r ^. #drawColHdr) of
-        (Nothing, _, _) -> emptyWidget
-        (_, Nothing, _) -> emptyWidget
-        (_, _, Nothing) -> emptyWidget
-        (Just colH, Just (colWs, colHdrH), Just dch) -> let
-          drawCol ci w = setAvailableSize (w, colHdrH) $ dch lf ci $ colH ci
+      MixedRenderers {drawCell} = r
+      MixedTabularList {list=l', calcWidthsPerRowKind=cWprk, widthsPerRow=wpr} = l
+      iH = l' ^^. L.listItemHeightL
+      colHdrRow wprk rhw' rhwd = case r ^. #colHdr of
+        Nothing -> emptyWidget
+        Just (MixedColHdr {draw, widths, height}) -> let
+          drawCol ci w = setAvailableSize (w, height) $ draw lf ci
           chrw = case r ^. #drawColHdrRowHdr of
             Nothing -> fill ' '
             Just dchrw -> dchrw lf rhwd
-          in setAvailableSize (rhw', colHdrH) chrw <+> hBox (zipWith drawCol [0..] colWs)
+          in setAvailableSize (rhw', height) chrw <+> hBox (zipWith drawCol [0..] $ widths wprk)
       renderRow wprk i f row = let
-        drawColumn ci w = wSet w $ drawCell lf (Position i f) row $ cell row ci
-        in hBox $ zipWith drawColumn [0..] $ wprk row
-      sizesAfterRowHdr rhw rows = case l ^. #sizes % #colSizes of
-        AvailWidth wf -> wf $ aW - rhw
-        VisibleRows wf -> case rows of
-          Nothing -> wf (visibleRows (l ^. #list) aH) $ aW - rhw
-          Just rows -> wf rows $ aW - rhw
-      renderList = let ColSizes {..} = sizesAfterRowHdr 0 Nothing
-        in render $ colHdrRow colHdr 0 0 <=> L.renderListWithIndex (renderRow rowKind) lf (l ^. #list)
-      renderHdrList rh rhw drh rows = let
+        drawColumn ci w = setAvailableSize (w, iH) $ drawCell lf (MixedContext (FlatContext i f) ci) row
+        in hBox $ zipWith drawColumn [0..] $ wpr wprk row
+      renderList = let wprk = cWprk aW $ visibleRows l' aH
+        in render $ colHdrRow wprk 0 0 <=> L.renderListWithIndex (renderRow wprk) lf l'
+      renderHdrList (RowHdr {draw, width, toRowHdr}) = let
+        (es, s) = visibleRowsWithStart l' aH
+        rhw = width aW $ zipWith toRowHdr es [s..]
         rhw' = min rhw aW
         rhwd = max 0 $ rhw - aW
-        ColSizes {..} = sizesAfterRowHdr rhw' rows
-        renderHdrRow i f r = wSet rhw' (drh lf rhwd (Position i f) r $ rh r i) <+> renderRow rowKind i f r
-        in render $ colHdrRow colHdr rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf (l ^. #list)
-  case (l ^. #contents % #rowHdr, l ^. #sizes % #rowHdr, r ^. #drawRowHdr) of
-    (Nothing, _, _) -> renderList
-    (_, Nothing, _) -> renderList
-    (_, _, Nothing) -> renderList
-    (Just rh, Just (FixedRowHeader w), Just drh) -> renderHdrList rh w drh Nothing
-    (Just rh, Just (AvailRowHeader w), Just drh) -> renderHdrList rh (w aW) drh Nothing
-    (Just rh, Just (VisibleRowHeaders w), Just drh) -> let (rs, rHs) = visibleRowsAndRowHdrs (l ^. #list) aH rh
-      in renderHdrList rh (w aW rHs) drh (Just rs)
+        wprk = cWprk (aW - rhw') es
+        renderHdrRow i f r = setAvailableSize (rhw', iH) (draw lf rhwd f $ toRowHdr r i) <+> renderRow wprk i f r
+        in render $ colHdrRow wprk rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf l'
+  maybe renderList renderHdrList $ r ^. #rowHdr
 
 -- | Handle events for mixed tabular list with navigation keys. This just calls 'L.handleListEvent'.
 handleMixedListEvent :: Ord n
   => Event -- ^ Event
-  -> EventM n (MixedTabularList n row cell rowH colH) ()
+  -> EventM n (MixedTabularList n e w) ()
 handleMixedListEvent e = zoom #list (L.handleListEvent e)
 
 -- | Handle events for mixed tabular list with vim keys. This just calls 'L.handleListEventVi'.
 handleMixedListEventVi :: Ord n
   => Event -- ^ Event
-  -> EventM n (MixedTabularList n row cell rowH colH) ()
+  -> EventM n (MixedTabularList n e w) ()
 handleMixedListEventVi e = zoom #list (L.handleListEventVi (\_ -> return ()) e)
diff --git a/src/Brick/Widgets/TabularList/Types.hs b/src/Brick/Widgets/TabularList/Types.hs
--- a/src/Brick/Widgets/TabularList/Types.hs
+++ b/src/Brick/Widgets/TabularList/Types.hs
@@ -1,63 +1,78 @@
 {-# LANGUAGE DeriveGeneric #-}
--- | Types shared by tabular list widgets
+{-# LANGUAGE NoFieldSelectors #-}
+-- | Types shared by tabular list widgets.
 --
 -- You don't have to import this module because modules for tabular list widgets re-export this module.
 module Brick.Widgets.TabularList.Types (
-  ColumnIndex
-, RowIndex
-, Index
+  Index
 , Width
 , Height
 , AvailWidth
 , ListItemHeight
-, Position(..)
+, FlatContext(..)
 , WidthDeficit
 , ListFocused
-, RowHeaderWidth(..)
+, Selected
+, RowHdr(..)
+, DrawColHdrRowHdr
 ) where
 
 -- base
 import GHC.Generics (Generic)
-
--- Types used by tabular list widgets
+-- brick
+import Brick.Types
 
--- | Index of column among columns
-type ColumnIndex = Int
--- | Index of row among rows
-type RowIndex = Int
--- | Index of element among elements
+-- | Index of a tabular list component among the same kind of components
 type Index = Int
--- | Width of a list element
+
+-- | Width of a tabular list component
 type Width = Int
--- | Height of a list element
+
+-- | Height of a tabular list component
 type Height = Int
+
 -- | Available width
 type AvailWidth = Int
--- | The fixed height for row headers and cells.
+
+-- | The fixed height for row headers and row columns.
 --
--- If the height of row headers or cells is not this height, then the list will look broken.
+-- If the height of row headers or row columns is not this height, then the list will look broken.
 type ListItemHeight = Int
--- | Linear position for tabular list elements.
-data Position = Position {
-  index :: Index -- ^ Index of item
-, selected :: Bool -- ^ Whether or not the item is selected
+
+-- | Context for one dimensional tabular list components
+data FlatContext = FlatContext {
+  index :: Index -- ^ Index of a component
+, selected :: Bool -- ^ Whether a component is selected
 } deriving (Show, Generic)
+
 -- | > widthDeficit = max 0 $ desiredWidth - availableWidth
 --
--- It is positive when an element is shrunk to the available width.
+-- It is positive when a column is shrunk to the available width.
 --
 -- If it is positive, you may want to remove paddings in your content because it is not followed or preceded by
 -- other columns.
 type WidthDeficit = Int
+
 -- | Whether the list is focused in an application
 type ListFocused = Bool
 
--- | Row header width information
-data RowHeaderWidth rowH =
-  -- | A fixed width for row header.
-  FixedRowHeader Width
-  -- | Calculate row header width with the width available for each row of a list
-  | AvailRowHeader (AvailWidth -> Width)
-  -- | Calculate row header width with visible row headers and the width available for each row of a list.
-  | VisibleRowHeaders (AvailWidth -> [rowH] -> Width) 
-  deriving Generic
+-- | Whether a tabular list component is selected
+type Selected = Bool
+
+-- | Row header
+--
+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
+-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
+data RowHdr n e r = RowHdr {
+  draw :: ListFocused -> WidthDeficit -> Selected -> r -> Widget n
+  -- | Calculate row header width from visible row headers and the width available for a list row.
+, width :: AvailWidth -> [r] -> Width
+  -- | Get a row header from a list row and row index.
+, toRowHdr :: e -> Index -> r
+} deriving Generic
+
+-- | The renderer for column header row header.
+--
+-- If row headers and column headers exist and 'DrawColHdrRowHdr' is 'Nothing', then column header row header is
+-- filled with empty space. 'DrawColHdrRowHdr' merely allows you to customize column header row header.
+type DrawColHdrRowHdr n = Maybe (ListFocused -> WidthDeficit -> Widget n)
