# Kotlin DataFrame resolves column types at compile time, and that changes the shape of the code

> Kotlin DataFrame is an Apache-2.0 licensed, JetBrains incubated Kotlin library for type-safe in-memory tabular data on the JVM, and its distinguishing feature is a compiler plugin that generates typed accessors for columns on the fly. The trade is that the plugin is required for the good parts, and the 1.0 line is still in release candidates.

**Kotlin/dataframe** — Kotlin DataFrame: typesafe in-memory structured data processing for JVM

- Repository: https://github.com/Kotlin/dataframe
- Website: https://kotlin.github.io/dataframe/home.html
- Stars: 1,071 · Forks: 87
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/kotlin-dataframe

## The compiler plugin generates the column accessors

The central claim of the project is about reconciling two things that normally fight. Kotlin DataFrame provides type-safe, in-memory processing of tabular data on the JVM, and it reconciles Kotlin's static typing with the dynamic nature of data, utilizing the full power of the Kotlin language with the interactive compiler plugin that provides type-safe access to columns by evaluating structure changes on the fly at compile time.

That mechanism is the whole idea. A dataframe has a schema, and the schema can change while your program runs, which is the situation that makes dataframes awkward in a statically typed language. Kotlin DataFrame answers it by having a compiler plugin inspect your code, work out what the dataframe contains at each point in the chain, and generate extension properties for the columns. So instead of a dynamic lookup by string name, you get something that looks like a property access, with the column's type, and the compiler checks it.

The feature list names the typesafe entry as on-the-fly generation of extension properties for type safe data access with Kotlin-style care for null safety. The link points at the extension properties API documentation.

The plugin is not optional garnish. The setup sections for both Gradle and Maven end by referring you to a page on setting up the compiler plugin, and there is a separate setup page for the plugin in a Gradle project. The Gradle instructions also mention a Custom Gradle Configuration page for when you do not need certain formats as dependencies, which tells you the formats are separate modules and therefore separate artifacts.

The project is also a JetBrains incubator project with the Kotlin component beta stability badge, and the Kotlin badge reads 2.4.21, so the plugin is tied to a specific compiler version family.

## Type compatibility comes from column schemas, not from the frame

One feature in the list is the most technical and the most useful to understand, because it determines when a function can accept a dataframe.

Polymorphic means that type compatibility derives from column schema compatibility. You can define a function that requires a special subset of columns in a dataframe but does not care about other columns.

This is a materially different model from ordinary generics. A function taking a DataFrame<T> in a conventional Kotlin library would demand one specific row type for the entire frame, and any extra or different column breaks it. Here, the contract is the set of columns and their types, and a frame with more columns still satisfies a function that needs three of them.

The consequence for your own code is that you can write narrow, reusable functions over columns without inventing wrapper types for every projection, and the compiler enforces that the needed columns exist with the right types.

The README is also candid about the current limitation of that model, and the sentence is worth quoting in substance. In notebooks this works out-of-the-box. In ordinary projects this requires casting, for now.

That is a real cost. A function written against a schema can be called directly from a Kotlin Notebook where the schema is inferred interactively, but in a normal Gradle or Maven project the compiler plugin has to be told more, and the README's own wording says casting is required for now. Any evaluation should test this path specifically, because a notebook demo and a production build are not the same experience with this library.

## Immutable frames and a chain of operations

The functional and immutable properties are what a dataframe in a JVM language has to get right, and the README states both plainly.

Functional means the data processing pipeline is organized in a chain of DataFrame transformation operations. Immutable means every operation returns a new instance of DataFrame, reusing underlying storage wherever it is possible.

That second clause is the important one, and it is the difference between a real implementation and a naive one. If every transformation copied the whole table, a pipeline of twenty operations would cost twenty copies, and the library would be unusable on real data. Reusing the underlying storage where the operation allows it is what makes chaining cheap, and it is also what makes the immutability safe: because a frame is never mutated in place, a reference held elsewhere keeps its old contents, and a pipeline can be reasoned about without defensive copies.

The rest of the list describes the shape of the API and the shape of the data. Hierarchical means it represents hierarchical data structures such as JSON or a tree of JVM objects. Generic means it can store objects of any type, not only numbers or strings. Interoperable means it is convertible with Kotlin data classes and collections, which also means conversion to and from other libraries' data structures is usually straightforward. Readable means the transformation operations are defined in a DSL close to natural language, which is the pandas-style ergonomics the project is explicitly inspired by, along with krangl and Kotlin Collections.

The documentation list is the map of that API: guides and examples, working with data schemas, the full list of supported operations, reading from SQL databases, reading and writing formats such as JSON, CSV and Apache Arrow, joining dataframes, the groupBy operation, and rendering to HTML for Jupyter notebooks.

## Each file format is its own module, so you only take what you need

The repository layout is the clearest statement of the project's scope, and it is a wide one.

The core/ directory holds the framework itself, and dataframe-compiler-plugin-core/ holds the part that does the type inference. Then there is a module per format or per integration: dataframe-csv/, dataframe-excel/, dataframe-json/, dataframe-arrow/, dataframe-jdbc/, dataframe-jupyter/, dataframe-geo/, dataframe-geo-jupyter/, dataframe-openapi/ and dataframe-openapi-generator/.

The geo and openapi modules are the interesting ones because they are not just format readers. A JDBC module means the dataframe can be a query result, and the openapi module with its generator means a schema can be produced from an OpenAPI definition, which is how a project that defines an API can get a typed dataframe without hand-writing the schema. The geo module and its Jupyter counterpart extend that to spatial data.

The build itself is a modern multi-module Gradle setup, with build-logic/, build-settings-logic/, plugins/, common-test-utils/, settings.gradle.kts and gradle.properties, plus a gradlew wrapper. There is a binder/ directory for the Binder environment, data/ and samples/ for fixtures and sample code, examples/ with notebooks and example projects, and docs/ for the documentation source.

The project also carries a set of process documents that are unusual for a library: MODULES.md, KDOC_DATA_TABLES.md, KDOC_GUIDELINES.md, KODEX_KDOC_PREPROCESSING.md, RELEASE_CHECK_LIST.md, a CODE_OF_CONDUCT.md, an AGENTS.md and a CLAUDE.md, plus a context7.json for the documentation tooling. The naming of those documents suggests a documentation pipeline that is generated and checked, and a release checklist that is followed.

## Setup is a dependency and a plugin

The installation instructions are the standard JVM ones, and the version to use is 1.0.0-rc02.

For Gradle, the dependency goes in build.gradle.kts.

```kotlin
dependencies {
    implementation("org.jetbrains.kotlinx:dataframe:1.0.0-rc02")
}
```

And the repository list needs mavenCentral().

```kotlin
repositories {
    mavenCentral()
}
```

For Maven, the equivalent goes in pom.xml.

```xml
<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>dataframe</artifactId>
    <version>1.0.0-rc02</version>
</dependency>
```

```xml
<repositories>
    <repository>
        <id>mavenCentral</id>
        <url>https://repo1.maven.org/maven2/</url>
    </repository>
</repositories>
```

The coordinates are org.jetbrains.kotlinx and dataframe, and the README links the Maven Central artifact page with a badge that tracks the latest release and the latest dev build, so you can see at a glance whether a newer version has been published than the one in the docs.

Three additional setup paths are documented and each addresses a different constraint. The Custom Gradle Configuration page is for when you do not need certain formats as dependencies, which is the module split made concrete. The Android page covers using the library in an Android project. And the IDEA Gradle example projects, along with a Gradle example project with the Kotlin DataFrame compiler plugin, give you a working configuration to copy rather than a description to interpret.

The other half of the setup, as both the Gradle and Maven sections note, is the compiler plugin, and the documentation has a page devoted to it in a Gradle project.

## Notebooks are the easy case, ordinary projects are the hard one

The README says the project integrates with Kotlin Notebook, and the documentation has a page on rendering to HTML for Jupyter notebooks. That integration is not a small feature, because a notebook is where a dataframe library earns its keep: you type an expression, the result is rendered as a table, and you keep the state in memory as you explore.

The same README is candid about where it is less smooth. The polymorphic feature, the one that lets a function require a subset of columns, works out-of-the-box in notebooks but requires casting in ordinary projects, for now. And the type-safe column access depends on the compiler plugin, which means the build has to be set up for it.

There is a second constraint hiding in the same area. The typesafe feature is about type safe access to columns, and the compatibility model is about column schemas. A schema has to come from somewhere, and the documentation has a page on working with data schemas as the first thing to understand. In practice that means a pipeline where the schema changes mid-chain, for example after a join that widens or narrows the columns, is exactly where the type system earns its keep, and exactly where you will spend time when it does not line up.

The third constraint is the project stage. The versions are 1.0.0-rc02 published on 2026-10-15, 1.0.0-rc01 on 2026-07-23 and 1.0.0-Beta5 on 2026-05-07, so the 1.0 line is in release candidates, the last push was on 2026-10-15, and the repository is not archived. The badges say JetBrains incubator project and Kotlin component beta stability, which is the status a library carries while its API is still settling, and the release candidates are the last stage before that changes.

## The alternative is pandas, and the difference is where the types are resolved

The README states its inspirations directly, and they are krangl, Kotlin Collections and pandas, which is an honest way to place the project. The comparison worth making is with pandas, since that is the model most people know.

In pandas, a dataframe is dynamically typed: the value type of a column is discovered at runtime, and errors surface when you run the line rather than at compile time. The ergonomics are wonderful and the failures are late.

Kotlin DataFrame moves that resolution to compile time. The schema is known, the column accessors are generated by the plugin, and a wrong column name or a wrong column type is a compile error. In exchange you need the compiler plugin, a plugin setup step in the build, and, today, casts in ordinary projects for the schema-polymorphism case.

That is the trade in one sentence, and it is the trade for a Kotlin team. In a JVM service where a data pipeline sits in the middle of an application with a type system around it, a compiler error on a column name is worth more than the dynamic convenience, because the failure arrives before deployment rather than in production.

The other alternative, which is not a dataframe library at all, is to use Kotlin collections and data classes directly. For a small amount of data, that is often the right answer, and the interoperability feature of the library is built for exactly that: you can convert a DataFrame to and from data classes and collections, so you can use the dataframe where the transformation is awkward and convert back to ordinary Kotlin where the business logic lives.

## Conclusion

Kotlin DataFrame fits a Kotlin team that wants dataframes in the same language and type system as the rest of its JVM code, and that is prepared to add a compiler plugin to the build. It does not fit a project that needs dynamic column access at runtime, because the type safety comes from a compile-time step that ordinary Kotlin projects do not have. Before adopting it, add the plugin to a branch and check that your column access compiles, and read the schemas documentation, since the schema is the contract that every operation in the pipeline depends on.

## FAQ

### How do I add Kotlin DataFrame to a Gradle project?

Add implementation("org.jetbrains.kotlinx:dataframe:1.0.0-rc02") to the dependencies block in build.gradle.kts and make sure mavenCentral() is in the repositories list. The setup documentation also covers configuring the compiler plugin, which is what provides the type-safe column accessors.

### What does the Kotlin DataFrame compiler plugin actually do?

It evaluates structure changes on the fly at compile time and generates extension properties for the columns, so column access is type safe with Kotlin-style null safety. The plugin is the mechanism behind the type safe feature, and both the Gradle and Maven setup sections point at a page on configuring it.

### How does polymorphism work in Kotlin DataFrame?

Type compatibility derives from column schema compatibility, so you can define a function that requires a specific subset of columns and does not care about the others. The README notes that this works out-of-the-box in notebooks, while in ordinary projects it requires casting for now.

### Which file formats does Kotlin DataFrame read and write?

The repository splits the formats into separate modules, including dataframe-csv, dataframe-excel, dataframe-json, dataframe-arrow and dataframe-jdbc, plus the geo and openapi modules and their generators. The documentation also covers reading from SQL databases and rendering to HTML for Jupyter notebooks.

### Is Kotlin DataFrame ready for production use?

The library is a JetBrains incubator project with the Kotlin component beta stability badge, and the current releases are 1.0.0-rc02 from 2026-10-15, 1.0.0-rc01 from 2026-07-23 and 1.0.0-Beta5 from 2026-05-07. It is licensed Apache-2.0, and the repository is not archived with the last push on 2026-10-15.

## Sources

- [Kotlin/dataframe on GitHub](https://github.com/Kotlin/dataframe)
- [License: Apache-2.0](https://github.com/Kotlin/dataframe/blob/master/LICENSE)
- [Project website](https://kotlin.github.io/dataframe/home.html)
- [README](https://github.com/Kotlin/dataframe/blob/master/README.md)
- [Releases](https://github.com/Kotlin/dataframe/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kotlin-dataframe
