Back to Scaling Ruby Applications guides

RailsAdmin vs ActiveAdmin: Centralized vs Modular Admin Configuration

Stanley Ulili
Updated on November 24, 2025

Admin panel structure decides where your settings live and how you manage them over time. RailsAdmin creates most screens automatically by reading your models and stores all configuration in one main file. ActiveAdmin takes a more manual approach, asking you to define each model in its own file using a simple DSL. The main difference is whether you want configuration kept in one place or split across smaller files.

RailsAdmin came out in 2010 as a Rails take on Django Admin. It reads your database and relationships to auto-build CRUD pages. ActiveAdmin arrived in 2011 from Greg Bell. It makes you register each resource on purpose, but gives more control through its per-resource DSL.

Today, you usually decide based on how you like to organize settings. RailsAdmin uses a single initializer so you can view everything at once. ActiveAdmin keeps files in app/admin/, right next to your app code, which can scale better as things grow. This affects how you find settings, how you work with others on changes, and whether you want a central or modular setup.

What is RailsAdmin?

Screenshot of the RailsAdmin page

RailsAdmin builds your admin by scanning ActiveRecord models when the app starts. After you mount the engine, each model shows up in the menu with list, show, edit, and delete pages. It reads column types, validations, and associations to pick the right inputs and formats. Aside from auth, you don’t need extra config to see results.

All customization goes in one initializer. You start with everything working, then add blocks to tweak models or fields. Model blocks control available actions. Field blocks set labels, help text, input types, and visibility. Because it’s all in one file, you can review the whole setup quickly.

You get standard CRUD plus search, filters, sorting, and exports. Associations like belongs_to and has_many are handled for you, including nested forms. File uploads work with ActiveStorage or CarrierWave. Bulk actions (like delete or export) are included.

RailsAdmin is a mountable engine that uses Bootstrap 3 and jQuery. Add the gem, run the installer, and mount it where you want. It works with Devise, Warden, or basic HTTP auth, and supports CanCanCan, Pundit, or custom authorization.

Minimal setup gets you running:

Gemfile
gem 'rails_admin', '~> 3.0'
config/routes.rb
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
config/initializers/rails_admin.rb
RailsAdmin.config do |config|
  config.authenticate_with do
    warden.authenticate! scope: :admin
  end

  config.authorize_with :cancancan
end

After this, go to /admin and you’ll see all models with working pages. Lists have sortable columns, show pages list attributes, edit forms respect validations, and delete has confirmations. Associations render as selects. Search works across text fields by default.

What is ActiveAdmin?

Screenshot of ActiveAdmin

ActiveAdmin builds your admin through explicit resource files. For each model you want, you add a file in app/admin/ that describes how it should look and behave. The DSL lets you define index pages, show pages, forms, filters, and custom actions. Nothing appears until you register it.

Settings live with each resource. Every model gets its own Ruby file where you pick columns, fields, filters, and behavior. Helpers exist for common needs like scopes, batch actions, and CSV exports. You can override anything for that resource in its file.

Because nothing is automatic, you declare everything you want to show. Index pages list only the columns you choose. Show pages present attributes in your order. Forms include only the fields you add. Filters are created from your declarations. The defaults are sensible, but you still decide what’s visible.

ActiveAdmin is also a Rails engine and plugs into the asset pipeline. You run a generator to add config and assets, then create resource files for the models you want. It ships with its own CSS and JS (customizable). Authentication is usually Devise. Authorization works with CanCanCan or Pundit.

Resource registration requires explicit code:

Gemfile
gem 'activeadmin'
 
rails generate active_admin:install
app/admin/products.rb
ActiveAdmin.register Product do
  permit_params :name, :price, :description, :category_id

  index do
    selectable_column
    id_column
    column :name
    column :price
    column :category
    actions
  end

  form do |f>
    f.inputs do
      f.input :name
      f.input :price
      f.input :description
      f.input :category
    end
    f.actions
  end
end

Here, ActiveAdmin.register sets up admin pages for Product. permit_params lists which attributes can be updated. The index block picks table columns. The form block defines form fields and layout. If you don’t declare it, it won’t show up.

RailsAdmin vs ActiveAdmin: quick comparison

Aspect RailsAdmin ActiveAdmin
Configuration style Single initializer file Distributed resource files
Generation approach Automatic introspection Explicit registration
Setup complexity Minimal, zero config start Moderate, requires generators
Default behavior All models exposed Only registered models
Customization location Centralized config file Individual resource files
Strong params Automatic from model Manual permit_params required
UI framework Bootstrap 3 + jQuery Custom CSS + jQuery
DSL philosophy Override defaults Define everything explicitly
File organization Single initializer File per resource in app/admin/
Navigation generation Automatic from models Automatic from registrations
Learning curve Gentle, works immediately Moderate, requires DSL knowledge
Community Smaller, less active Larger, very active
Customization depth Configuration-based Ruby code in resource files

Configuration organization

Let's start by examining how each framework organizes configuration. RailsAdmin consolidates everything into config/initializers/rails_admin.rb. All model configurations, field customizations, and action definitions live in this single file. Product configuration sits alongside Order configuration alongside User configuration. Finding any configuration means opening one file and searching for the model name.

config/initializers/rails_admin.rb
RailsAdmin.config do |config|
  config.model 'Product' do
    # Product configuration
  end

  config.model 'Order' do
    # Order configuration
  end

  config.model 'User' do
    # User configuration
  end
end

This centralization works well initially but becomes unwieldy as the admin grows. The file reaches 500+ lines quickly with multiple models. Navigating requires scrolling past unrelated configurations. Working on one model means seeing configuration for all models.

ActiveAdmin distributes configuration across app/admin/. Each model gets its own file: products.rb, orders.rb, users.rb. Product configuration stays isolated from Order configuration. Opening products.rb shows only Product admin code.

app/admin/products.rb
ActiveAdmin.register Product do
  # All Product configuration here
end
app/admin/orders.rb
ActiveAdmin.register Order do
  # All Order configuration here
end

This distribution mirrors Rails' controller organization: one file per concern. Files stay focused and manageable. Finding configuration requires knowing which file to open, but each file remains digestible.

List view customization

Moving from configuration organization to how data gets displayed, list views reveal each framework's core philosophy. RailsAdmin shows every column automatically, requiring exclusion of unwanted fields:

config/initializers/rails_admin.rb
config.model 'Product' do
  list do
    field :name
    field :price
    field :category

    exclude_fields :description, :created_at, :updated_at, :internal_notes
  end
end

The configuration declares visible fields, then explicitly excludes fields that shouldn't appear. The exclude_fields list grows as unwanted columns get discovered. New database columns appear automatically until excluded.

ActiveAdmin requires explicit declaration of every visible column:

app/admin/products.rb
index do
  column :name
  column :price
  column :category
  actions
end

Nothing appears unless declared in the index block. New database columns stay invisible until added. The approach requires more initial typing but prevents surprise columns from appearing.

For computed values or custom formatting, both support block syntax, but the location differs. RailsAdmin puts these in the centralized initializer within nested blocks. ActiveAdmin keeps them in the resource file where all Product logic lives.

Form customization

After configuring list views, building forms shows similar patterns at work. RailsAdmin generates forms from all model attributes automatically:

config/initializers/rails_admin.rb
config.model 'Product' do
  edit do
    field :name
    field :description, :text
    field :price
    field :category

    exclude_fields :internal_notes, :featured_score
  end
end

Every attribute appears unless explicitly excluded. Field types get inferred from column types, with manual overrides for special cases like textareas. Internal fields need explicit exclusion.

ActiveAdmin requires declaring every form field:

app/admin/products.rb
permit_params :name, :description, :price, :category_id

form do |f|
  f.inputs "Product Details" do
    f.input :name
    f.input :description
    f.input :price
    f.input :category
  end
  f.actions
end

The permit_params declaration controls allowed attributes. The form block specifies every field and its type. Grouping fields into sections happens through f.inputs blocks with labels. More typing but complete control over form structure.

Custom layouts like side-by-side fields require different approaches. RailsAdmin needs view template overrides. ActiveAdmin allows inline HTML within the form DSL using helper methods, keeping customizations in the resource file.

Handling associations

Looking at how associations get managed reveals the difference between automatic detection and explicit configuration. RailsAdmin detects belongs_to and has_many associations automatically:

config/initializers/rails_admin.rb
config.model 'Order' do
  edit do
    field :user
    field :line_items do
      nested_form true
    end
  end
end

The user association renders as a dropdown. The nested_form true option enables inline editing for line items with bundled JavaScript. Works immediately but customizing the nested form JavaScript proves difficult since it's internal to the gem.

ActiveAdmin requires explicit configuration:

app/admin/orders.rb
permit_params :user_id, :status,
              line_items_attributes: [:id, :product_id, :quantity, :_destroy]

form do |f|
  f.inputs "Order Details" do
    f.input :user
    f.input :status
  end

  f.inputs "Line Items" do
    f.has_many :line_items, allow_destroy: true do |li|
      li.input :product
      li.input :quantity
    end
  end
  f.actions
end

The permit_params includes line_items_attributes for nested attributes. The f.has_many helper creates nested forms with explicit options. Each nested field gets declared individually. Adding custom JavaScript to nested forms works through standard Stimulus controllers or similar approaches.

Search and filtering

Now let's examine how search functionality works in each framework to understand their different approaches to data filtering. RailsAdmin provides automatic text search across all string and text columns:

config/initializers/rails_admin.rb
config.model 'Product' do
  list do
    field :name
    field :price
  end
end

A search box appears without configuration, searching across all text fields. Works immediately but provides no control over search behavior or advanced filtering options.

ActiveAdmin requires explicit filter declarations:

app/admin/products.rb
filter :name
filter :sku
filter :price
filter :category
filter :created_at
filter :in_stock, as: :boolean

Each filter declaration creates an appropriate input: text fields for strings, dropdowns for associations, date pickers for timestamps, checkboxes for booleans. Users apply multiple filters simultaneously, with each refining results cumulatively. Complete control over which fields become filterable and how.

Scopes and batch actions

Moving from individual filters to preset filters and bulk operations shows how each framework handles common admin workflows. RailsAdmin lacks native scope support, requiring custom actions for preset filters.

ActiveAdmin treats scopes as first-class features:

app/admin/products.rb
scope :all, default: true
scope :published
scope :out_of_stock
scope :low_stock, -> { where("quantity < ?", 10) }

Scope buttons appear above tables. Custom scopes accept lambda blocks for arbitrary filters. The pattern matches ActiveRecord scopes directly.

For batch actions, RailsAdmin includes batch delete and export by default but requires complex configuration for custom batch operations through nested blocks in the initializer.

ActiveAdmin makes batch actions look like controller methods:

app/admin/products.rb
batch_action :publish do |ids|
  Product.find(ids).each(&:publish!)
  redirect_to collection_path, notice: "Products published"
end

batch_action :export_csv do |ids|
  # Export logic
end

The batch_action blocks contain plain Ruby. Selected IDs get accessed, operations get performed, redirects happen. Standard Rails patterns throughout.

Custom actions and routes

Beyond batch operations, individual record actions demonstrate how each framework handles extensibility. RailsAdmin uses nested custom_action blocks in the initializer:

config/initializers/rails_admin.rb
config.model 'Product' do
  custom_action :duplicate do
    http_methods :post
    controller do
      # Duplication logic
    end
  end
end

The custom action lives in the initializer, separated from other controller code. Testing happens through RailsAdmin's abstractions.

ActiveAdmin adds member actions directly in resource files:

app/admin/products.rb
member_action :duplicate, method: :post do
  # Duplication logic
end

action_item :duplicate, only: :show do
  link_to "Duplicate", duplicate_admin_product_path(resource), method: :post
end

The member_action creates routes. The action_item adds buttons. Both live in the resource file. Code reads like controller actions. Testing uses standard controller specs.

Dashboard customization

Turning to dashboards shows how each framework approaches creating custom overview pages. RailsAdmin provides a basic dashboard that requires view overrides for customization. Creating app/views/rails_admin/main/dashboard.html.haml becomes necessary, potentially breaking on gem updates.

ActiveAdmin treats dashboards as regular resources:

app/admin/dashboard.rb
ActiveAdmin.register_page "Dashboard" do
  content do
    columns do
      column do
        panel "Recent Products" do
          # Recent products display
        end
      end

      column do
        panel "Statistics" do
          # Statistics display
        end
      end
    end
  end
end

The register_page method creates custom pages. The DSL provides layout helpers. Ruby code fetches and renders data. The dashboard file lives alongside resource files. No view overrides needed.

UI customization and theming

Looking at visual customization reveals fundamental architectural differences. RailsAdmin uses Bootstrap 3 as a mountable engine. Changing styling requires overriding Sass variables or writing CSS targeting internal markup. View customization needs creating overrides that mirror gem structure, potentially breaking on updates.

ActiveAdmin generates modifiable views:

 
rails generate active_admin:views

This creates app/views/active_admin/ with all framework views. Layouts get modified directly. Styling happens through standard asset pipeline:

app/assets/stylesheets/active_admin.scss
@import "active_admin/base";

.active_admin {
  #header { background: #2c3e50; }
}

Views and styles live in the application, not the gem. Changes persist across updates.

Community and ecosystem

Finally, examining the plugin ecosystem shows how community size affects available solutions. RailsAdmin has limited third-party plugins:

Gemfile
gem 'rails_admin_import'
gem 'rails_admin_rollincode'

The smaller community means fewer pre-built solutions. Stack Overflow questions sometimes go unanswered.

ActiveAdmin has extensive plugins:

Gemfile
gem 'activeadmin_addons'
gem 'active_admin_import'
gem 'activeadmin_reorderable'
gem 'activeadmin_quill_editor'

The large, active community provides plugins for common needs. Stack Overflow has thousands of answered questions. GitHub issues get quick attention.

Final thoughts

RailsAdmin and ActiveAdmin focus on different goals. At first, RailsAdmin stands out for its speed and simplicity. It gives you a working admin panel almost instantly with no setup, which makes it ideal for small projects and basic CRUD operations. However, as your application grows, its single configuration file becomes harder to manage, and the automatic setup starts to limit flexibility.

In contrast, ActiveAdmin takes more time to configure at the start, but the extra effort pays off later. Each model has its own file, customization follows familiar Rails patterns, and the structure stays organized even as the app becomes more complex.