Library / SDK
bogdan/datagrid avatar
bogdan/datagrid

Datagrid builds sortable and filterable table reports for Ruby apps

Gem to create tables grids with sortable columns and filters

1,113 stars111 forksRubyMIT

At a glance

What is it?
Datagrid is a Ruby gem that turns a model into a report with filters, sortable columns and CSV export, defined through a small class-level DSL. It supports several ORMs and lets you keep the query logic in Ruby rather than in a controller, which is convenient for admin panels and painful when you need something the DSL does not express.
Who is it for?
Datagrid suits a Ruby team that needs a reporting or admin screen over a model, wants the filter and sort parameters to be a typed API rather than a bag of strings in the controller, and values the CSV export that comes with it. It does not suit a screen whose behaviour must diverge from the standard report, because everything is expressed through the grid DSL and the built-in views.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 101 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Datagrid is a report generator built on a class

Datagrid is a Ruby library for generating reports, and the project description lists the audience of those reports as admin panels, analytics and data browsers. The features it names are filtering, columns, sort order, localization and export to CSV.

The way it works is by subclassing. A grid class declares three things: the scope of objects to look through, the filters available to the user, and the columns to display. The class then becomes both the definition and the runtime object, and a report is an instance of it built with the filter values:

ruby
class UsersGrid < Datagrid::Base

  scope do
    User.includes(:group)
  end

  filter(:category, :enum, select: ["first", "second"])
  filter(:disabled, :xboolean)
  filter(:group_id, :integer, multiple: true)
  filter(:logins_count, :integer, range: true)
  filter(:group_name, :string, header: "Group") do |value|
    self.joins(:group).where(groups: {name: value})
  end

  column(:name)
  column(:group, order: -> { joins(:group).order(groups: :name) }) do |user|
    user.name
  end
  column(:active, header: "Activated") do |user|
    !user.disabled
  end

end

The philosophy stated in the project documentation is that the DSL complements object oriented design rather than replacing it, that it is extensible in every way while providing a lot of defaults, and that if your ORM supports an operation then datagrid supports it too.

That last point is the practical one. The gem supports ActiveRecord, Mongoid, MongoMapper, Sequel, and plain Array for smaller in-memory data sets, so the same grid definition can sit on top of a relational database or a document store.

Filters carry the typecasting, which is the real value

A filter definition has four parts according to the documentation: the name of the filter, the type used for value typecasting, a conditions block that applies to the defined scope, and additional options.

The type list is where most of the value sits. Datagrid supports text, integer, float, date, datetime, boolean, xboolean, enum, string and dynamic filters. The xboolean type is described as a select of yes, no and any, and the enum type is a selection of the given values, so those two arrive with a built-in form control. An integer filter can be declared as a range or as accepting multiple values, and a filter can carry a header so the label on the form does not have to match the attribute name.

The dynamic type is the one to know about, because it is the escape hatch. A filter with a conditions block of your own applies an arbitrary scope to the query, which in the example is a join on the group with a name match. That is how you filter on something that is not a column of the model, and it is also how you express a filter the built-in types cannot.

The reason this matters is typecasting. A hand-rolled admin screen receives filter values as strings from the query string and has to cast them itself, and a filter named `logins_count` that accepts a range is fiddly to do by hand. Here the type is declared once and the cast follows from it, which is why the report object in the next section takes values like `logins_count: [1, nil]` and knows what they mean.

Columns own their sort order

Each column is represented by a name and a code block that calculates the value, and formatting options are available on top of that. Every column is sortable.

The sorting detail is where the design shows. A column can declare an `order` expression that is used for the SQL sort, which is not always the same as the value shown. In the example, the `group` column displays `user.name` inside the block but sorts by joining the group and ordering on the group name:

ruby
column(:group, order: -> { joins(:group).order(groups: :name) }) do |user|
  user.name
end

That separation is what makes the sort correct. If the displayed value were used for sorting, the ordering would be on the wrong table and would either fail or produce a different order from the one the user sees.

Columns can also be positioned, with an `after:` option naming the column to follow, and labelled independently of the attribute with a `header:` option. Both are small things, and both are the kind of thing that would otherwise be a template override, which is the point of a DSL in this position.

Since the code block receives the record, a column can compute anything the ORM can compute, including a derived boolean such as `!user.disabled` presented as an `Activated` header.

The report object is the public API

The grid class is instantiated with the filter values, and the instance is what the controller and the view talk to:

ruby
report = UsersGrid.new(
  group_id: [1,2],
  logins_count: [1, nil],
  category: "first",
  order: :group,
  descending: true
)

report.assets # => Array of User instances
report.header # => ["Name", "Group", "Activated"]
report.rows   # => array of row arrays
report.data   # => [ header, *rows]
report.to_csv # => Yes, it is

The `assets` method returns the records themselves, the query result, and the documentation shows the equivalent SQL in a comment so you can see the shape of what was run. `header` and `rows` return the display form, and `data` combines them into a single array with the header first, which is the shape a table or a spreadsheet wants.

`to_csv` is the feature that most often justifies the dependency. A report screen needs an export button, and the combination of the same filter values producing both a table and a CSV is exactly what a hand-rolled implementation has to duplicate.

The API surface is documented as Rdoc, with separate pages for the core scope, the columns, the filters, the frontend helper and the configuration, and there is a live demo application with its source code linked from the project documentation.

A generator scaffolds the grid, the controller and the view

Datagrid ships a Rails generator, and running it produces a working screen rather than a snippet:

sh
rails g datagrid:scaffold skills

The documented output is a grid class in `app/grids/skills_grid.rb`, a controller in `app/controllers/skills_controller.rb`, a view in `app/views/skills/index.html.erb`, a route for the resources, and a line inserted into the application stylesheet. That is the whole stack for a report screen in one command, which is what makes the library attractive for back office work.

When the built-in views are not enough, they can be copied into the application so you have a control over them:

sh
rails g datagrid:views

After that, the files live in your project and you own them, and the documentation points to the frontend section for the advanced helpers, which covers building the form and table yourself rather than using what the gem ships.

That two step arrangement is a deliberate trade. Copying the views is a one way door, because after the copy you are maintaining markup that has to keep working as the gem changes, and the migration guide for version 2 is the place to look before a major upgrade. The built-in views are the cheap path, and the copied ones are the escape hatch.

Where the grid DSL is the wrong tool

The limitation is structural and worth naming before you commit. Everything a report can do has to be expressed in the grid DSL, and the set of things the DSL can express is finite. A filter that needs a different join, a column that needs a computation the ORM cannot express, a summary row that is not a column, a cell that needs to be a link or a button, an export in a format other than CSV, none of these are part of the grid definition, and they have to be handled in the view or by overriding the built-in views.

The second limit is the nature of the abstraction. Datagrid is a report generator, not a data table component. It produces a filtered, sorted set of records and a table of them, and it does not attempt to be a general purpose interactive grid with inline editing, row selection, drag reordering or client side sorting. Teams looking for that kind of component are looking for a different category of library, and no amount of DSL configuration will get there.

There is also a size consideration that the documentation hints at rather than states. The Array support is described as in-memory data of smaller scale, which tells you the gem expects to push a scope down to the database and let the database do the filtering and sorting. A grid over a large table with a wide-open scope will push the whole table through the query layer, and that is a database design problem rather than a grid problem, but the grid is where you will notice it.

The genuine alternative is a hand-written controller and view, where you read filter values from the params, build a scope yourself, and render a table with whatever markup you want. The difference in approach is that datagrid moves the typecasting, the naming and the export into Ruby objects that you can unit test, and gives up the freedom to do something the DSL does not model. For an internal list that will be the same shape for two years, the trade is clearly good. For a screen that becomes its own product, the freedom wins.

Editorial conclusion

Datagrid suits a Ruby team that needs a reporting or admin screen over a model, wants the filter and sort parameters to be a typed API rather than a bag of strings in the controller, and values the CSV export that comes with it. It does not suit a screen whose behaviour must diverge from the standard report, because everything is expressed through the grid DSL and the built-in views. Check first the ORM you need is on the supported list and the filter type you want exists, since a custom filter block is the escape hatch when it does not.

Frequently asked questions

Which ORMs does Datagrid support?

The gem lists ActiveRecord, Mongoid, MongoMapper, Sequel and Array for in-memory data of smaller scale. The stated philosophy is that if your ORM supports an operation, datagrid supports it too.

How do I create a Datagrid grid in a Rails app?

Run `rails g datagrid:scaffold skills`, which creates the grid class in `app/grids/`, a controller, an index view, a route and a stylesheet line. To take over the built-in views, run `rails g datagrid:views` to copy them into the application.

How do I sort a Datagrid column by a different field than the one it displays?

Give the column an `order` expression separate from its display block, for example `column(:group, order: -> { joins(:group).order(groups: :name) }) do |user| user.name end`. The displayed value and the SQL sort are then independent, and every column is sortable.

Can Datagrid export a report to CSV?

Yes, the report object exposes `to_csv`, alongside `assets` for the records, `header` and `rows` for the display form, and `data` for the header and rows combined.

Official sources

  1. bogdan/datagrid on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bogdan-datagrid.svg)](https://hysenlabs.com/projects/bogdan-datagrid)

Was this useful?