diff --git a/dataframe-core.cabal b/dataframe-core.cabal
--- a/dataframe-core.cabal
+++ b/dataframe-core.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.4
 name:               dataframe-core
-version:            2.0.0.0
+version:            2.1.0.0
 synopsis:           Core data structures for the dataframe library.
 description:
     Minimal interchange-format types for the @dataframe@ ecosystem:
@@ -28,10 +28,16 @@
         -Wunused-local-binds
         -Wunused-packages
 
-library internal
+library
     import:             warnings
-    visibility:         public
     exposed-modules:
+                        DataFrame.Core
+                        DataFrame.Typed.Freeze
+                        DataFrame.Typed.Generic
+                        DataFrame.Typed.Record
+                        DataFrame.Typed.Schema
+                        DataFrame.Typed.Types
+                        DataFrame.Typed.Util
                         DataFrame.Errors
                         DataFrame.Operators
                         DataFrame.Display.Terminal.Colours
@@ -68,24 +74,5 @@
                         random >= 1 && < 2,
                         text >= 2.1 && < 3,
                         vector >= 0.13 && < 0.15
-    hs-source-dirs:     src-internal
-    default-language:   Haskell2010
-
-library
-    import:             warnings
-    exposed-modules:
-                        DataFrame.Core
-                        DataFrame.Typed.Freeze
-                        DataFrame.Typed.Generic
-                        DataFrame.Typed.Record
-                        DataFrame.Typed.Schema
-                        DataFrame.Typed.Types
-                        DataFrame.Typed.Util
-    reexported-modules: DataFrame.Errors,
-                        DataFrame.Operators
-    build-depends:      base >= 4 && < 5,
-                        text >= 2.1 && < 3,
-                        vector >= 0.13 && < 0.15,
-                        dataframe-core:internal
-    hs-source-dirs:     src
+    hs-source-dirs:     src, src-internal
     default-language:   Haskell2010
diff --git a/src-internal/DataFrame/Internal/DataFrame.hs b/src-internal/DataFrame/Internal/DataFrame.hs
--- a/src-internal/DataFrame/Internal/DataFrame.hs
+++ b/src-internal/DataFrame/Internal/DataFrame.hs
@@ -341,19 +341,23 @@
 getRowAsText df i = map (`showElement` i) (V.toList (columns df))
 
 showElement :: Column -> Int -> T.Text
-showElement (BoxedColumn _ (c :: V.Vector a)) i = case c V.!? i of
-    Nothing -> error $ "Column index out of bounds at row " ++ show i
-    Just e
-        | Just Refl <- testEquality (typeRep @a) (typeRep @T.Text) -> e
-        | App t1 t2 <- typeRep @a
-        , Just HRefl <- eqTypeRep t1 (typeRep @Maybe) ->
-            case testEquality t2 (typeRep @T.Text) of
-                Just Refl -> fromMaybe "null" e
-                Nothing -> stripJust (T.pack (show e))
-        | otherwise -> T.pack (show e)
-showElement (UnboxedColumn _ c) i = case c VU.!? i of
-    Nothing -> error $ "Column index out of bounds at row " ++ show i
-    Just e -> T.pack (show e)
+showElement (BoxedColumn bm (c :: V.Vector a)) i = case bm of
+    Just b | not (bitmapTestBit b i) -> "null"
+    _ -> case c V.!? i of
+        Nothing -> error $ "Column index out of bounds at row " ++ show i
+        Just e
+            | Just Refl <- testEquality (typeRep @a) (typeRep @T.Text) -> e
+            | App t1 t2 <- typeRep @a
+            , Just HRefl <- eqTypeRep t1 (typeRep @Maybe) ->
+                case testEquality t2 (typeRep @T.Text) of
+                    Just Refl -> fromMaybe "null" e
+                    Nothing -> stripJust (T.pack (show e))
+            | otherwise -> T.pack (show e)
+showElement (UnboxedColumn bm c) i = case bm of
+    Just b | not (bitmapTestBit b i) -> "null"
+    _ -> case c VU.!? i of
+        Nothing -> error $ "Column index out of bounds at row " ++ show i
+        Just e -> T.pack (show e)
 showElement (PackedText bm p) i = case bm of
     Just b | not (bitmapTestBit b i) -> "null"
     _ -> packedIndexText p i
