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.0 -- 2023-02-20
+
+* Add drawColHdrRowHdr
+
 ## 0.1.0.2 -- 2023-02-10
 
 * Fixed documentation
diff --git a/brick-tabular-list.cabal b/brick-tabular-list.cabal
--- a/brick-tabular-list.cabal
+++ b/brick-tabular-list.cabal
@@ -1,25 +1,25 @@
-cabal-version:      3.0
-name:               brick-tabular-list
-version:            0.1.0.2
+cabal-version: 3.0
+name: brick-tabular-list
+version: 0.2.0.0
 
 synopsis: Tabular list widgets for brick.
 
 description:
-  This package contains two tabular list widgets for brick.
+    This package contains two tabular list widgets for brick.
 
-  * Grid tabular list
-  * Mixed tabular list
+    * Grid tabular list
+    * Mixed tabular list
 
-  == To get started
+    == 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.
+    * 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.
 
-  == Lens support
+    == Lens support
 
-  If you want to use lens, I encourage using @OverloadedLabels@ extension with generic-lens or optics-core.
+    If you want to use lens, I encourage using @OverloadedLabels@ extension with generic-lens or optics-core.
 
-  For zoom, you have to use van Laarhoven lens because brick supports zoom through microlens.
+    For zoom, you have to use van Laarhoven lens because brick supports zoom through microlens.
 
 homepage: https://codeberg.org/amano.kenji/brick-tabular-list
 bug-reports: https://codeberg.org/amano.kenji/brick-tabular-list/issues
@@ -43,7 +43,26 @@
     Description: Build demonstration programs
     Default: False
 
+common all
+    build-depends:
+        base >=4.15.1.0 && <5
+      , brick >=1.5 && <1.7
+      , containers ^>=0.6.4
+      , 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
@@ -53,84 +72,29 @@
         Brick.Widgets.TabularList.Internal.Common
       , Brick.Widgets.TabularList.Internal.Lens
     build-depends:
-        base >=4.15.1.0 && <4.18
-      , 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
+        generic-lens ^>=2.2.1
     hs-source-dirs: src
 
+common mixed-tabular-list
+    import: exec
+    other-modules: Internal.MixedTabularList
+
 executable mixed-tabular-list
-    if !flag(demos)
-      buildable: False
+    import: mixed-tabular-list
     main-is:          MixedTabularList.hs
-    other-modules:
-        Internal.MixedTabularList
-    build-depends:
-        base >=4.15.1.0 && <4.18
-      , brick >=1.5 && <1.7
-      , brick-tabular-list
-      , containers ^>=0.6.4
-      , microlens ^>=0.4.13
-      , optics-core ^>=0.4.1
-      , vty ^>=5.38
-    hs-source-dirs:   demos
-    default-language: Haskell2010
-    ghc-options:      -threaded
 
 executable mixed-tabular-list-vi
-    if !flag(demos)
-      buildable: False
+    import: mixed-tabular-list
     main-is:          MixedTabularListVi.hs
-    other-modules:
-        Internal.MixedTabularList
-    build-depends:
-        base >=4.15.1.0 && <4.18
-      , brick >=1.5 && <1.7
-      , brick-tabular-list
-      , containers ^>=0.6.4
-      , microlens ^>=0.4.13
-      , optics-core ^>=0.4.1
-      , vty ^>=5.38
-    hs-source-dirs:   demos
-    default-language: Haskell2010
-    ghc-options:      -threaded
 
+common grid-tabular-list
+    import: exec
+    other-modules: Internal.GridTabularList
+
 executable grid-tabular-list
-    if !flag(demos)
-      buildable: False
+    import: grid-tabular-list
     main-is: GridTabularList.hs
-    other-modules:
-        Internal.GridTabularList
-    build-depends:
-        base >=4.15.1.0 && <4.18
-      , brick >=1.5 && <1.7
-      , brick-tabular-list
-      , containers ^>=0.6.4
-      , microlens ^>=0.4.13
-      , optics-core ^>=0.4.1
-      , vty ^>=5.38
-    hs-source-dirs: demos
-    default-language: Haskell2010
-    ghc-options: -threaded
 
 executable grid-tabular-list-vi
-    if !flag(demos)
-      buildable: False
+    import: grid-tabular-list
     main-is: GridTabularListVi.hs
-    other-modules:
-        Internal.GridTabularList
-    build-depends:
-        base >=4.15.1.0 && <4.18
-      , brick >=1.5 && <1.7
-      , brick-tabular-list
-      , containers ^>=0.6.4
-      , microlens ^>=0.4.13
-      , optics-core ^>=0.4.1
-      , vty ^>=5.38
-    hs-source-dirs: demos
-    default-language: Haskell2010
-    ghc-options: -threaded
diff --git a/demos/Internal/GridTabularList.hs b/demos/Internal/GridTabularList.hs
--- a/demos/Internal/GridTabularList.hs
+++ b/demos/Internal/GridTabularList.hs
@@ -137,18 +137,16 @@
 }
 
 drh lf wd (Position i f) row = \case
-  Nothing -> hCenter $ str " "
+  Nothing -> fill ' '
   Just rh ->
     let attrFn = if f
           then id
           else withAttr rowHdrAttr
-        rp = if wd > 0 then 0 else 1
-    in attrFn $ padRight (Pad rp) $ padLeft Max (str $ show rh)
+    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)
 
 dch lf wd (Position i f) = \case
-  Nothing -> hCenter $ str " "
-  Just ch -> let rp = if wd > 0 then 0 else 1 in
-    withAttr columnHdrAttr $ padRight (Pad rp) $ padRight Max (str ch)
+  Nothing -> fill ' '
+  Just ch -> withAttr columnHdrAttr $ padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str ch)
 
 renderers :: LibraryRenderers
 renderers = GridRenderers {
@@ -157,18 +155,18 @@
       then withAttr colSelectedAttr
       else id
     in attrFn $ case mc of
-      Nothing -> hCenter $ str " "
-      Just (StringCell s) -> let rp = if wd > 0 then 0 else 1 in
-        padRight (Pad rp) $ padRight Max (str s)
+      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
-        rp = if wd > 0 then 0 else 1
-        in padRight (Pad rp) $ padRight Max $ str time'
+        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
diff --git a/demos/Internal/MixedTabularList.hs b/demos/Internal/MixedTabularList.hs
--- a/demos/Internal/MixedTabularList.hs
+++ b/demos/Internal/MixedTabularList.hs
@@ -131,8 +131,7 @@
     let attrFn = if f
           then id
           else withAttr rowHdrAttr
-        rp = if wd > 0 then 0 else 1
-    in attrFn $ padRight (Pad rp) $ padLeft Max (str $ show rh)
+    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)
 
 renderers :: LibraryRenderers
 renderers = MixedRenderers {
@@ -148,6 +147,8 @@
       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 ()
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
@@ -26,7 +26,7 @@
 --
 -- > setAvailableSize (width, listItemHeight)
 --
--- A rendering function for column headers is given a space with
+-- A rendering function for column headers and column header row header is given a space with
 --
 -- > setAvailableSize (width, columnHeaderHeight)
 --
@@ -37,17 +37,19 @@
 --
 -- The following examples show how a rendering function can claim the available width.
 --
--- > let rp = if widthDeficit > 0 then 0 else 1
--- > in padRight (Pad rp) $ padLeft Max content
+-- > padRight (Pad $ if widthDeficit > 0 then 0 else 1) $ padLeft Max content
 --
 -- > padRight Max content <+> str " "
 --
--- > let lp = if widthDeficit > 0 then 0 else 1
--- > in padLeft (Pad lp) $ hCenter content
+-- > padLeft (Pad $ if widthDeficit > 0 then 0 else 1) $ hCenter 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.
+--
+-- 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
 --
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
@@ -87,6 +87,7 @@
   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)
 } deriving Generic
 
 -- | Sizes for elements of grid tabular list.
@@ -150,38 +151,61 @@
   }
   deriving Show
 
--- | Calculate visible columns from the width available for columns. If there aren't enough columns to the left side
--- to show the current column in the center, LeftAnchor is returned. If there are enough columns to the left side of
--- the current column, then check whether there are enough columns of the right side of the current column to show the
--- current column in the center. If there are enough columns to the right side, MiddleColumns is returned. If there
--- aren't enough columns to the right side, then try to calculate the leftmost visible column for AnchoredRight.
--- If there aren't enough columns to fill the availble width for AnchoredRight, then AnchoredLeft is returned.
+-- | 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 l aW = let curCol = l ^. #currentColumn in
   case S.splitAt curCol (l ^. #sizes % #row) of
+    -- If current column is outside the boundary of row columns, return `NoColumn`.
     (_, Empty) -> NoColumn
     (left, cW :<| right) -> if aW <= 0
+      -- If the available width is 0 or less than 0,
       then NoColumn
+      -- 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.
       else let
+        -- The amount of space to the left of the current column shown in the center.
         lW = (aW - cW) `div` 2
+        -- The amount of space to the right of the current column shown in the center.
         rW = aW - lW - cW
+        -- Calculate the leftmost visible column for the current column shown in the center.
         leftForMiddle (l :|> w) idx accW = if accW+w < lW
+          -- If the leftmost visible column hasn't been reached, go to the left by one column.
           then leftForMiddle l (idx-1) (accW+w)
+          -- If the leftmost visible column has been reached, calculate the rightmost visible column.
           else rightForMiddle idx (accW+w) right (curCol+1) 0
+        -- If there aren't enough columns to the left of the current column shown in the center, calculate the rightmost
+        -- visible column for the first column shown at the left corner.
         leftForMiddle Empty _ accW = rightForLeft right (curCol+1) (accW+cW)
+        -- Calculate the rightmost visible column for the current column shown in the center.
         rightForMiddle li lAccW (w :<| r) ri accW = if accW+w < rW
+          -- If the rightmost visible column hasn't been reached, go to the right by one column.
           then rightForMiddle li lAccW r (ri+1) (accW+w)
+          -- If the rightmost visible column has been reached, return 'MiddleColumns'.
           else MiddleColumns { left = li, right = ri, offset = lAccW-lW, totalWidth = lAccW+cW+accW+w }
+        -- If there aren't enough columns to the right of the current column shown in the center, calculate the leftmost
+        -- visible column for the last column shown at the right corner.
         rightForMiddle _ _ Empty _ accW = leftForRight left (curCol-1) (accW+cW)
+        -- Calculate the rightmost visible column for the first column shown at the left corner.
         rightForLeft (w :<| r) idx accW = if accW+w < aW
+          -- If the rightmost visible column hasn't been reached, go to the right by one column.
           then rightForLeft r (idx+1) (accW+w)
+          -- If the rightmost visible column has been reached, return 'AnchoredLeft'.
           else AnchoredLeft idx
+        -- If there aren't enough columns to fill the available width with the first column at the left corner, return
+        -- 'AnchoredLeft' with the last column as the rightmost visible column.
         rightForLeft Empty idx _ = AnchoredLeft (idx-1)
+        -- Calculate the leftmost visible column for the last column shown at the right corner.
         leftForRight (l :|> w) idx accW = if accW+w < aW
+          -- If the leftmost visible column hasn't been reached, go to the left by one column.
           then leftForRight l (idx-1) (accW+w)
+          -- If the leftmost visible column has been reached, return 'AnchoredRight'.
           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
         in leftForMiddle left (curCol-1) 0
 
@@ -222,26 +246,28 @@
       aH = c^^.availHeightL
       iH = list ^. #listItemHeight
       wSet = setAvailableSize . (, iH)
-      colHdrRow vCs rhw = case (l ^. #contents % #colHdr, l ^. #sizes % #colHdr, r ^. #drawColHdr) of
+      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
-          wSet = setAvailableSize . (, colHdrH)
-          drawCol wd c w = wSet w $ dch lf wd (Position c (c == curCol)) $ colH c
-          in wSet rhw (fill ' ') <+> renderColumns l vCs drawCol
+          drawCol wd c w = setAvailableSize (w, colHdrH) $ dch lf wd (Position c (c == curCol)) $ colH c
+          chrw = case r ^. #drawColHdrRowHdr of
+            Nothing -> fill ' '
+            Just dchrw -> dchrw lf rhwd
+          in setAvailableSize (rhw', colHdrH) chrw <+> renderColumns l vCs drawCol
       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
       renderList = let vCs = visibleColumns l aW in
-        render $ colHdrRow vCs 0 <=> L.renderListWithIndex (renderRow vCs) lf list
+        render $ colHdrRow vCs 0 0 <=> L.renderListWithIndex (renderRow vCs) lf list
       renderHdrList rh rhw drh = let
         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' <=> L.renderListWithIndex renderHdrRow lf list
+        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
diff --git a/src/Brick/Widgets/TabularList/Internal/Lens.hs b/src/Brick/Widgets/TabularList/Internal/Lens.hs
--- a/src/Brick/Widgets/TabularList/Internal/Lens.hs
+++ b/src/Brick/Widgets/TabularList/Internal/Lens.hs
@@ -7,7 +7,6 @@
 
 import Lens.Micro ((^.))
 
-
 (^^.) = (^.)
 {-# INLINE (^^.) #-}
 infixl 8 ^^.
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
@@ -72,6 +72,7 @@
   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)
 } deriving Generic
 
 -- | Column sizes calculated after row header width is calculated.
@@ -136,14 +137,16 @@
       aH = c^^.availHeightL
       iH = l ^. #list % #listItemHeight
       wSet = setAvailableSize . (, iH)
-      colHdrRow sizes rhw = case (l ^. #contents % #colHdr, sizes, r ^. #drawColHdr) of
+      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
-          wSet = setAvailableSize . (, colHdrH)
-          drawCol ci w = wSet w $ dch lf ci $ colH ci
-          in wSet rhw (fill ' ') <+> hBox (zipWith drawCol [0..] colWs)
+          drawCol ci w = setAvailableSize (w, colHdrH) $ dch lf ci $ colH ci
+          chrw = case r ^. #drawColHdrRowHdr of
+            Nothing -> fill ' '
+            Just dchrw -> dchrw lf rhwd
+          in setAvailableSize (rhw', colHdrH) chrw <+> hBox (zipWith drawCol [0..] colWs)
       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
@@ -153,13 +156,13 @@
           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 <=> L.renderListWithIndex (renderRow rowKind) lf (l ^. #list)
+        in render $ colHdrRow colHdr 0 0 <=> L.renderListWithIndex (renderRow rowKind) lf (l ^. #list)
       renderHdrList rh rhw drh rows = let
         rhw' = min rhw aW
-        rwhd = max 0 $ rhw - aW
+        rhwd = max 0 $ rhw - aW
         ColSizes {..} = sizesAfterRowHdr rhw' rows
-        renderHdrRow i f r = wSet rhw' (drh lf rwhd (Position i f) r $ rh r i) <+> renderRow rowKind i f r
-        in render $ colHdrRow colHdr rhw' <=> L.renderListWithIndex renderHdrRow lf (l ^. #list)
+        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
@@ -170,9 +173,13 @@
       in renderHdrList rh (w aW rHs) drh (Just rs)
 
 -- | Handle events for mixed tabular list with navigation keys. This just calls 'L.handleListEvent'.
-handleMixedListEvent :: Ord n => Event -> EventM n (MixedTabularList n row cell rowH colH) ()
+handleMixedListEvent :: Ord n
+  => Event -- ^ Event
+  -> EventM n (MixedTabularList n row cell rowH colH) ()
 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 -> EventM n (MixedTabularList n row cell rowH colH) ()
+handleMixedListEventVi :: Ord n
+  => Event -- ^ Event
+  -> EventM n (MixedTabularList n row cell rowH colH) ()
 handleMixedListEventVi e = zoom #list (L.handleListEventVi (\_ -> return ()) e)
