PowerGrid is a production-ready, server-side processed table component for Rails applications. It combines the raw performance of Active Record with the responsiveness of modern UI frameworks like Hotwire and Stimulus.
Built with ViewComponent, Turbo Frames, Stimulus, and TailwindCSS, PowerGrid offers a premium, SPA-like experience without the complexity of a Javascript frontend framework.
- π Server-Side Processing: Efficiently handles huge datasets using Active Record
offsetandlimit. - β‘οΈ Hotwire Integration: Instant sorting, filtering, and pagination updates via Turbo Frames.
- π Advanced Search:
- Multi-column search support.
- Search on joined tables via
sql_expression. - Automatic input debouncing.
- π Advanced Filtering:
- Support for Search, Select, Checkboxes, and Radio Buttons.
- Dynamic collections using Lambdas (e.g.,
-> { User.all }). - Custom filtering logic via blocks.
- π Smart Pagination:
- Numbered pagination window (e.g.,
1 2 ... 5 6 7 ... 10). - Dynamic Per Page limits.
- Numbered pagination window (e.g.,
- π Secure Scoping: Supports
initial_scopefor tenant/user scoping (e.g.,current_user.posts). - π¨ Visuals:
- Professional "Slate" color palette.
- Fully responsive and Dark Mode compatible.
- Customizable CSS classes.
- π Modular Toolbar:
- Render the toolbar (search/filters) separately from the table for flexible layouts.
- π Optimization: Built-in support for
includesto automatically prevent N+1 queries.
Add this line to your application's Gemfile:
gem 'power_grid'And then execute:
$ bundle installCreate a class that inherits from PowerGrid::Base. This class defines your data source, columns, and filters.
# app/grids/users_grid.rb
class UsersGrid < PowerGrid::Base
scope { User.all }
# Basic columns
column :name, sortable: true, searchable: true
column :email, sortable: true, searchable: true
# Column with block formatting (for badges, links, etc.)
column :status do |user|
tag.span(user.status.humanize, class: "badge badge-#{user.status}")
end
# Filters
filter :role, collection: ["Admin", "User"], include_blank: "All Roles"
# Advanced Filter (Checkbox with custom logic)
filter :department_ids, type: :checkbox, collection: -> { Department.pluck(:name, :id) } do |scope, value|
scope.where(department_id: value)
end
endInitialize the grid in your controller action. You can pass an initial scope (like current_user.posts) to ensure security.
# app/controllers/users_controller.rb
def index
# @grid = UsersGrid.new(params)
# OR with scoping:
@grid = UsersGrid.new(params, initial_scope: current_user.posts)
endRender the PowerGrid::TableComponent, passing the grid instance.
<!-- app/views/users/index.html.erb -->
<%= render PowerGrid::TableComponent.new(@grid) %>PowerGrid supports rich filtering options beyond simple text search.
Use type: :checkbox to render a list of checkboxes.
filter :category_ids, header: "Categories", type: :checkbox, collection: -> { Category.pluck(:name, :id) }Use type: :radio to render radio buttons.
filter :active, type: :radio, collection: [["Yes", true], ["No", false]]Pass a lambda or Proc to collection: to load options dynamically at request time.
filter :manager_id, collection: -> { User.where(role: 'manager').pluck(:name, :id) }Pass a block to filter to customize how the query is modified.
filter :query_date, type: :text do |scope, value|
# Parse date string and filter range
date = Date.parse(value) rescue nil
date ? scope.where(created_at: date.all_day) : scope
endPowerGrid uses TailwindCSS by default but allows you to override classes for deep customization. Pass a css: hash to the component.
<%= render PowerGrid::TableComponent.new(@grid, css: {
table: "min-w-full divide-y divide-gray-200 border border-gray-300",
th: "px-6 py-3 bg-blue-50 text-left text-xs font-medium text-blue-500 uppercase tracking-wider",
tr: "bg-white hover:bg-blue-50 transition-colors duration-150"
}) %>Available keys in DEFAULT_CSS: container, table, thead, tbody, tr, th, td, pagination, page_link, page_link_active, page_prev, page_next, page_gap, search_input, filter_select, filter_input.
You can render the toolbar (Search, Filters, Per Page) separately from the table. This is useful for placing filters in a sidebar or sticky header.
-
Disable the built-in toolbar in
TableComponent:<%= render PowerGrid::TableComponent.new(@grid, toolbar: false) %>
-
Render
ToolbarComponentwhere you want it:<div class="sticky top-0 bg-white z-10 shadow p-4"> <%= render PowerGrid::ToolbarComponent.new(@grid) %> </div>
Defines a column in the table.
| Option | Type | Description |
|---|---|---|
sortable |
boolean |
If true, the column header will be clickable to sort. |
searchable |
boolean |
If true, the global search input will check this column using LIKE. |
sql_expression |
string |
The raw SQL column name/expression to use for sorting/searching. Essential for joined tables (e.g., "posts.title"). |
includes |
symbol/array |
Association(s) to eager load when rendering this column to prevent N+1 queries. |
params: The Railsparamshash (required for sorting/filtering state).initial_scope: Use this to pass context-aware relations, such ascurrent_account.invoices. If provided, it overrides the class-levelscope.
PowerGrid creates HTML with standard Tailwind utility classes (using the slate color palette). Ensure your Tailwind configuration scans your gem paths.
Ensure your application has turbo-rails and stimulus-rails installed.
The gem includes a Stimulus controller power_grid--table_controller which handles search input debouncing and auto-submission.
Bug reports and pull requests are welcome on GitHub at https://github.com/bhavinNandani/power_grid.
The gem is available as open source under the terms of the MIT License.
