JSAdmin

JSAdmin is a mountable Rails engine that exposes Active Record models through a generic CRUD UI. It discovers models from the host Rails application and builds forms from database column metadata.

It is built for Rails 8.1+, Tailwind CSS, and Hotwire-compatible Rails helpers.

Installation

Add this line to your application's Gemfile:

gem "js_admin"

Install and mount the engine:

bundle install
bin/rails generate js_admin:install
bin/rails tailwindcss:engines

The install generator mounts JSAdmin at /admin, pins JSAdmin's JavaScript in config/importmap.rb, and imports the engine Tailwind entrypoint when app/assets/tailwind/application.css exists. You can choose another mount path:

bin/rails generate js_admin:install --path=/backoffice
bin/rails tailwindcss:engines

JSAdmin uses importmap-rails for JavaScript loading. Rails 8 default apps already include config/importmap.rb; if it was removed, run bin/rails importmap:install before running js_admin:install.

The generator adds these pins when they are missing:

pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "js_admin", to: "js_admin/application.js", preload: true
pin "js_admin/controllers/theme_controller", to: "js_admin/controllers/theme_controller.js", preload: true
pin "js_admin/controllers/flash_controller", to: "js_admin/controllers/flash_controller.js", preload: true

Usage

Open the mounted path:

/admin

The home page is intentionally empty. Model navigation lives in the sidebar. By default, JSAdmin exposes every concrete Active Record model with an existing database table. Namespaced models are grouped under namespace dropdowns. No per-model field setup is required.

The UI includes a fixed header, fixed desktop sidebar, dark mode toggle, compact pagination controls, and inline form validation errors. The default page size is 10 records.

The install generator also creates config/initializers/js_admin.rb with commented configuration examples. JSAdmin works without changing that file.

Available configuration options:

Option Default Description
included_models [] Model names/classes to expose. Empty means every discovered concrete Active Record model.
excluded_models [] Model names/classes to hide after discovery.
records_per_page 10 Number of records shown per resource index page.
association_limit 100 Maximum number of associated records loaded into a belongs_to select.
display_name_methods %i[name title email username id] Methods tried in order when displaying records in tables and select labels.
basic_auth_username nil HTTP Basic Auth username. Auth is disabled unless username and password are both present.
basic_auth_password nil HTTP Basic Auth password.
basic_auth_realm "JSAdmin" HTTP Basic Auth realm.

Example:

# config/initializers/js_admin.rb
JSAdmin.configure do |config|
  config.include_models User, Post
  config.exclude_models AuditLog
  config.records_per_page = 10
end

Authentication

JSAdmin works without authentication unless Basic Auth credentials are configured. When both username and password are set, every JSAdmin request requires HTTP Basic Auth.

# config/initializers/js_admin.rb
JSAdmin.configure do |config|
  config.basic_auth_username = ENV["JS_ADMIN_BASIC_AUTH_USERNAME"]
  config.basic_auth_password = ENV["JS_ADMIN_BASIC_AUTH_PASSWORD"]
  config.basic_auth_realm = "JSAdmin"
end

For production apps, you can also mount JSAdmin inside the host application's admin authentication constraint when you need app-level authorization.

Field Mapping

JSAdmin maps database columns to inputs automatically:

Column metadata Input
text textarea
boolean checkbox
integer, bigint, float, decimal number input
date, datetime, time matching date/time input
Rails enum select
belongs_to foreign key select
other columns text input

Primary keys, timestamps, STI inheritance columns, readonly attributes, and generated columns are not editable.

Hotwire Scope

JSAdmin depends on importmap-rails, turbo-rails, and stimulus-rails so it works naturally in modern Rails apps using the Rails default Hotwire setup.

The admin shell keeps the header and sidebar stable while the main CRUD area is wrapped in a Turbo Frame. Sidebar navigation, resource links, forms, and pagination update that frame instead of replacing the whole page. Delete actions can respond with Turbo Streams to refresh the flash region and resource list.

Stimulus controllers manage the dark mode toggle and auto-dismiss flash messages. The selected theme is persisted in storage and a cookie so the engine layout can render the initial dark class without inline JavaScript.

Validation Errors

Form validation errors are rendered inside the form next to the matching field. JSAdmin renders form controls directly so Rails' default field_with_errors wrapper does not break the Tailwind grid layout. Errors that do not belong to an editable field are shown at the top of the form.

Development

Run the test suite:

bundle install
bundle exec rails test

Build the gem locally:

gem build js_admin.gemspec

Dummy App

This repository includes test/dummy as the Rails engine test app. It is used by the test suite and should stay small.

You can run it manually for functional checks:

bin/rails db:prepare
bin/rails server

Open:

http://localhost:3000/admin

Sandbox App

Use sandbox/ when you want to verify the real installation flow in a fresh Rails app. The sandbox directory is ignored by Git.

Create a sandbox app wired to this local checkout:

bin/setup_sandbox

The setup script creates several demo models, including a namespaced model, seeds sample records, and runs an initial Tailwind build.

Run a one-off browser check:

cd sandbox
bin/rails server

For iterative UI/CSS development, run the Rails server and Tailwind watcher together:

cd sandbox
bin/dev

Open:

http://localhost:3000/admin

Rebuild it from scratch:

rm -rf sandbox
bin/setup_sandbox

Release

Releases are published to RubyGems through GitHub Actions and RubyGems Trusted Publishing. To release a new version, update lib/js_admin/version.rb, update CHANGELOG.md, and push a matching tag:

git tag v0.0.2
git push origin v0.0.2

License

The gem is available as open source under the terms of the MIT License.