Installation
Get RapidRails UI up and running in your Rails application in under 5 minutes.
Prerequisites
- Ruby 3.0+
- Rails 7.0+
Tailwind CSS: The installer will set up Tailwind CSS v4 automatically if not already installed.
After purchasing, you'll receive:
- 1. Gemfury Token - Access to download the gem
- 2. License Key - Required for production deployments
Step 1: Configure Bundler
Run this command to save your Gemfury credentials (one-time setup):
bundle config gem.fury.io YOUR_GEMFURY_TOKEN
Replace YOUR_GEMFURY_TOKEN with the token from your purchase confirmation email.
Note: This stores credentials in ~/.bundle/config (not in your project), keeping them out of version control.
Heads up: If you skip this step, Bundler will show an error asking for username:password. Don't worry - just use your token with the command above. No username needed.
Step 2: Add to Gemfile
Add the gem source and dependency to your Gemfile:
# Gemfile
source "https://gem.fury.io/rapidrailsui" do
gem "rapid_rails_ui"
end
Then run:
bundle install
Step 3: Run the Installer
Run the installer to configure everything automatically:
rails generate rapid_rails_ui:install
The installer will set up Tailwind CSS, dark mode, Stimulus controllers, and all necessary configuration.
After installation: Restart your Rails server to load the new components.
Step 4: Set License Key (Production Only)
License validation is automatically skipped in development and test environments. You can start building immediately!
For production, set the environment variable on your hosting platform:
export RAPID_RAILS_UI_LICENSE_KEY=RRUI-PRO-YOURKEY-S1-D5-20261231-CHECKSUM
Or use Rails credentials:
# config/credentials.yml.enc
rapid_rails_ui:
license_key: RRUI-PRO-YOURKEY-S1-D5-20261231-CHECKSUM
# config/application.rb
ENV['RAPID_RAILS_UI_LICENSE_KEY'] = Rails.application.credentials.dig(:rapid_rails_ui, :license_key)
Step 5: Verify Installation
Add a button to any view to confirm everything works:
<%= rui_button("Hello World", color: :success) %>
You should see:
Done! You're ready to start building.
CI/CD Configuration
For automated deployments, configure your CI environment with the Gemfury token:
GitHub Actions
env:
BUNDLE_GEM__FURY__IO: ${{ secrets.GEMFURY_TOKEN }}
Heroku
heroku config:set BUNDLE_GEM__FURY__IO=your-gemfury-token
Docker
ARG GEMFURY_TOKEN
RUN bundle config gem.fury.io ${GEMFURY_TOKEN}
Kamal
Kamal uses Docker BuildKit secrets to securely pass credentials during image build.
1. Add to .kamal/secrets:
export BUNDLE_GEM__FURY__IO="your-gemfury-token"
export RAPID_RAILS_UI_LICENSE_KEY="RRUI-PRO-YOURKEY-..."
2. Add to config/deploy.yml:
builder:
secrets:
- BUNDLE_GEM__FURY__IO
env:
secret:
- RAPID_RAILS_UI_LICENSE_KEY
3. Update your Dockerfile:
# Install gems with Gemfury credentials
RUN --mount=type=secret,id=BUNDLE_GEM__FURY__IO \
if [ -f /run/secrets/BUNDLE_GEM__FURY__IO ]; then \
bundle config set --global gem.fury.io "$(cat /run/secrets/BUNDLE_GEM__FURY__IO)"; \
fi && \
bundle install
Note: The .kamal/secrets file should be in your .gitignore. Each developer/deployer creates their own locally.
Build vs Runtime: builder.secrets passes credentials during Docker image build (for bundle install). env.secret injects variables at container runtime (for license validation).
Advanced Setups
The installer covers the most common Rails configurations (Tailwind v4 + importmap). The scenarios below address less standard setups. Skip this section unless one of them applies to your app.
Tailwind v3 with a class prefix configured
If your tailwind.config.js sets a prefix: option, your Tailwind build emits prefixed classes (for example tw-bg-zinc-900) while RapidRailsUI components emit unprefixed classes (bg-zinc-900). The two CSS outputs do not match.
The recommended workaround: a second Tailwind build dedicated to the gem, with no prefix, served alongside your main app CSS. Four steps:
1. Add a dedicated config:
const { execSync } = require('child_process')
const rapidRailsUIPath = execSync('bundle show rapid_rails_ui').toString().trim()
module.exports = {
darkMode: ['selector', '[data-theme="dark"]'],
content: [
`${rapidRailsUIPath}/app/components/**/*.{rb,erb,js}`,
`${rapidRailsUIPath}/lib/rapid_rails_ui/helpers/**/*.{rb,erb,js}`,
'./app/javascript/controllers/rapid_rails_ui/**/*.js',
],
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
}
2. Add a dedicated input CSS:
@tailwind base;
@tailwind components;
@tailwind utilities;
3. Append the build to your package.json:
"build:css": "...your existing pipeline... && tailwindcss -c ./config/tailwind.rapid_rails_ui.config.js -i ./app/assets/stylesheets/rapid_rails_ui.tailwind.css -o ./app/assets/builds/rapid_rails_ui.css --minify",
"build:css:watch": "...your existing watcher... & tailwindcss -c ./config/tailwind.rapid_rails_ui.config.js -i ./app/assets/stylesheets/rapid_rails_ui.tailwind.css -o ./app/assets/builds/rapid_rails_ui.css -w"
4. Link the dedicated stylesheet in your layout, after your prefixed Tailwind:
<%= stylesheet_link_tag "rapid_rails_ui" %>
No safelist needed. As of v0.45.0 the gem ships _tailwind_manifest.rb which Tailwind picks up via the content glob above. The dedicated build outputs around 480 KB minified covering every class the components can emit.
esbuild as your JavaScript bundler
As of v0.46.0 the install generator detects your bundler from the Gemfile (importmap-rails vs jsbundling-rails) and emits the right import path style automatically. importmap hosts get bare specifiers; jsbundling hosts get relative paths so esbuild/webpack/rollup resolve them without manual edits.
import AccordionController from "./rapid_rails_ui/accordion_controller"
Force the path style explicitly if your Gemfile lists both gems or auto-detection picks the wrong one:
rails g rapid_rails_ui:install --bundler=jsbundling
On v0.45.x and earlier you needed a one-line sed after install: sed -i '' 's|from "controllers/rapid_rails_ui/|from "./rapid_rails_ui/|g' app/javascript/controllers/index.js (Linux: drop the ''). v0.46.0+ does this for you.
Existing Stimulus controllers with the same class names
If your app already declares classes like AccordionController, CheckboxController, DialogController, MenuController, or similar, esbuild will fail with "The symbol X has already been declared". As of v0.46.0 the install generator accepts a flag that prefixes the JS local variable names of every gem controller import so both your controllers and the gem's coexist:
rails g rapid_rails_ui:install --namespace-imports=Rui
Result:
import RuiAccordionController from "./rapid_rails_ui/accordion_controller"
import RuiCheckboxController from "./rapid_rails_ui/checkbox_controller"
import RuiDialogController from "./rapid_rails_ui/dialog_controller"
application.register("accordion", RuiAccordionController)
application.register("checkbox", RuiCheckboxController)
application.register("dialog", RuiDialogController)
The flag accepts any PascalCase string. Rui matches the gem's rui_* Ruby helper convention while following JS PascalCase. Stimulus IDs (data-controller="accordion") are not affected by the flag.
Stimulus ID conflicts (separate from class-name conflicts): if your app already registers a Stimulus ID the gem also uses (e.g. "checkbox", "dialog", "menu", "search"), the registrations collide. The gem's component templates currently hardcode data-controller="checkbox" etc., so namespacing only the gem's Stimulus ID would silently break the gem's components. Until the gem ships configurable Stimulus IDs (planned for v0.47.0), prefer renaming your host's controller to free up the natural ID.
Host has global input[type=...] styling
Several RapidRailsUI components (checkbox button/pill/card variants, switch, combobox, dropdown, live search) hide a native <input> behind a styled wrapper using Tailwind's sr-only peer classes. The native input is supposed to be invisible while still accepting clicks and keyboard events.
If your app declares an unscoped global rule like:
input[type="checkbox"]{
width: 21px;
height: 21px;
border: 2px solid $card-border;
background: $accent;
/* ... */
}
CSS specificity makes input[type="checkbox"] (0,1,1) win over the gem's .sr-only rule (0,1,0). The native input renders visibly alongside the styled gem wrapper, producing duplicate or misaligned controls.
Surgical fix: exclude the gem's hidden inputs from the host's global rule by adding :not(.sr-only):not(.peer) to every selector. Original styling is preserved for the host's own checkboxes (which do not use those Tailwind utility classes).
input[type="checkbox"]: not(.sr-only):not(.peer){
width: 21px;
height: 21px;
/* ... */
}
input[type="checkbox"]: not(.sr-only):not(.peer):checked{ /* ... */ }
input[type="checkbox"]: not(.sr-only):not(.peer):disabled{ /* ... */ }
input[type="checkbox"]: not(.sr-only):not(.peer):hover{ /* ... */ }
/* repeat for every selector that targets input[type="checkbox"] */
The same pattern applies to any input[type="radio"], input[type="text"], or input[type="search"] rule that uses an attribute selector without a class scope. Audit your global stylesheets after install if you see component visual regressions.
Why this is host side: the gem cannot know about your host's global selectors. The fix has to live in the host's CSS so existing app behavior stays intact while gem components opt out via the Tailwind utility classes they ship.