# Technical Documentation: seyhunak/twitter-bootstrap-rails

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/seyhunak/twitter-bootstrap-rails)

### Overview (`README.md`)

# twitter-bootstrap-rails

[](http://badge.fury.io/rb/twitter-bootstrap-rails)
[](https://github.com/seyhunak/twitter-bootstrap-rails/stargazers)
[](https://github.com/seyhunak/twitter-bootstrap-rails/network)
[](https://github.com/seyhunak/twitter-bootstrap-rails/issues)

Integrates Bootstrap 5.3.8 into Rails Asset Pipeline. Supports Rails 8, 7, 6, 5.

## Install

```ruby
# Gemfile
gem "twitter-bootstrap-rails"
```

```bash
bundle install
rails generate bootstrap:install static
```

## Generators

Every code snippet on the
[Bootstrap 5.3 Introduction page](https://getbootstrap.com/docs/5.3/getting-started/introduction/)
is reproducible from a command:

| Docs snippet | Command |
|---|---|
| Starter template | `rails g bootstrap:starter` |
| CDN CSS `<link>` | `rails g bootstrap:cdn --css` |
| CDN JS bundle `<script>` | `rails g bootstrap:cdn --js` |
| Popper + `bootstrap.js` separately | `rails g bootstrap:cdn --separate-popper` |
| Important globals (doctype, `lang`, viewport) | baked into every generated layout and the starter template |

```bash
# Install Bootstrap assets into the asset pipeline
rails g bootstrap:install static

# ...or load Bootstrap from the jsDelivr CDN instead
rails g bootstrap:install cdn

# Generate a Bootstrap layout
rails g bootstrap:layout application

# ...linking Bootstrap from the CDN, with Subresource Integrity
rails g bootstrap:layout application --cdn

# ...using Popper and bootstrap.js separately rather than the bundle
rails g bootstrap:layout application --cdn --separate-popper

# Write the starter template from the Bootstrap docs
rails g bootstrap:starter                    # -> public/bootstrap-starter.html
rails g bootstrap:starter --path=public/demo.html

# Print CDN tags to paste into a layout you already own (writes nothing)
rails g bootstrap:cdn

# Generate themed views for scaffold
rails g scaffold Task title:string done:boolean
rails db:migrate
rails g bootstrap:themed Tasks
```

`bootstrap:install cdn` records the choice in `config/initializers/bootstrap.rb`,
so `bootstrap:layout` emits CDN tags by default afterwards. Pass `--cdn` or
`--no-cdn` to override it per run.

Both asset pipelines are supported. On **Sprockets**, `static` adds `require`
directives to your manifests. On **Propshaft** (the Rails 8 default) there are no
directives to add, so the generated layout links the vendored files directly —
run `rails g bootstrap:layout` after `bootstrap:install static` either way.

The Bootstrap version and its CDN URLs and integrity hashes live in one place,
`Twitter::Bootstrap::Rails::BOOTSTRAP_CDN`. No template hardcodes them.

## Snippets

Every snippet published at
[getbootstrap.com/docs/5.3/examples](https://getbootstrap.com/docs/5.3/examples/)
— 62 of them across 12 categories — is a command. Each writes an `.html.erb`
partial into `app/views/shared/`:

```bash
rails g bootstrap:header               # the first variant, "centered"
rails g bootstrap:header dark_search   # a specific variant
rails g bootstrap:header --list        # what's available, with descriptions
```

| Command | Variants |
|---|---|
| `rails g bootstrap:header` | `centered`, `nav_pills`, `with_auth_buttons`, `dark_search`, `light_search`, `grid_dropdown`, `double`, `dark_double` |
| `rails g bootstrap:hero` | `centered`, `centered_screenshot`, `with_image`, `signup_form`, `cropped_image`, `dark` |
| `rails g bootstrap:features` | `columns_with_icons`, `hanging_icons`, `custom_cards`, `icon_grid`, `with_title` |
| `rails g bootstrap:sidebar` | `dark`, `light`, `icon_only`, `collapsible`, `list_group` |
| `rails g bootstrap:footer` | `simple`, `with_brand`, `with_nav`, `columns`, `with_newsletter` |
| `rails g bootstrap:dropdown` | `simple`, `with_search`, `with_icons`, `calendar`, `mega_menu` |
| `rails g bootstrap:list_group` | `with_avatars`, `checkboxes`, `checkboxes_expanded`, `checkable`, `radios` |
| `rails g bootstrap:modal` | `sheet`, `confirm`, `whats_new`, `signup` |
| `rails g bootstrap:badge` | `pills`, `subtle`, `subtle_bordered`, `with_avatar`, `with_icons`, `with_avatar_divider` |
| `rails g bootstrap:breadcrumb` | `basic`, `with_icons`, `chevron`, `custom` |
| `rails g bootstrap:button` | `pills`, `grid`, `with_icons`, `loading`, `circle` |
| `rails g bootstrap:jumbotron` | `with_icon`, `placeholder`, `full_width`, `basic` |

Variants are listed in the order they appear on the docs page, so the first one
in each row is the first example Bootstrap shows.

Options, on every snippet command:

| Option | Effect |
|---|---|
| `--list` | Print the variants with descriptions and exit, writing nothing |
| `--as=NAME` | Partial name to write (default: the generator name) |
| `--path=DIR` | Where to write it (default: `app/views/shared`) |
| `--no-icons` | Skip installing the shared icon sprite |

```bash
rails g bootstrap:footer columns --as=site_footer --path=app/views/layouts
# -> app/views/layouts/_site_footer.html.erb
```

### Placeholder images

Five snippets (`hero centered`, `hero centered_screenshot`, `hero with_image`,
`hero cropped_image`, `features custom_cards`) reference placeholder images that
only exist on Bootstrap's docs site, so they 404 until you point them at your own
assets. The generator warns you when you pick one.

### Icons

36 of the 62 snippets use Bootstrap Icons through an SVG sprite. Those commands
also write `_bootstrap_icons.html.erb` next to the partial. Render it **once**,
near the top of `<body>` in your layout, or those icons come out as empty boxes:

```erb
<%= render "shared/bootstrap_icons" %>
```

## Quick Start

```bash
rails new myapp
cd myapp
echo 'gem "twitter-bootstrap-rails"' >> Gemfile
bundle install
rails generate bootstrap:install static
rails generate scaffold Task title:string description:text completed:boolean
rails db:migrate
rails generate bootstrap:themed Tasks
rails generate bootstrap:layout application
rails server
```

Run `bootstrap:layout` even if you already have a layout — that step is what puts
Bootstrap's CSS and JS on the page.

## Upgrading to 5.4.0

5.4.0 is a breaking release:

- **Less is gone.** `rails g bootstrap:install less` no longer exists; the
  argument is now `static` (default) or `cdn`. Bootstrap 5 has no Less build, and
  the `less-rails` and `execjs` dependencies have been dropped.
- **Glyphicons are gone**, including the `glyph` helper. Bootstrap dropped them in
  v4. The snippet generators ship Bootstrap Icons instead.
- **CoffeeScript support is gone**, along with `--no-coffeescript`.
- The gem version no longer tracks the Bootstrap version.
  `Twitter::Bootstrap::Rails::BOOTSTRAP_VERSION` says which Bootstrap ships.

If you are on Rails 8, note that `bootstrap:install static` previously left
Propshaft apps with no Bootstrap at all — that is fixed here. See the
[CHANGELOG](CHANGELOG.md) for the full list.

## Sponsor

[](https://opencollective.com/twitter-bootstrap-rails)
[](https://www.patreon.com/cw/seyhunak)

- [Open Collective](https://opencollective.com/twitter-bootstrap-rails) — back the project with a one-off or monthly contribution
- [Patreon](https://www.patreon.com/cw/seyhunak) — become a patron
- [Bootstrap 5 for Rails](https://www.patreon.com/seyhunak/posts/twitter-rails-5-165533952) — release write-up on Patreon

## Buy me a Coffee
## ☕ Support the Project

[](https://www.buymeacoffee.com/seyhunak)

## Contributors

Thanks to all contributors who are helping to make better.

<a href="https://github.com/seyhunak/twitter-bootstrap-rails/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=seyhunak/twitter-bootstrap-rails" />
</a>

### Star History

<a href="https://www.star-history.com/#seyhunak/twitter-bootstrap-rails&Date">
 <picture>
   <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=seyhunak/twitter-bootstrap-rails&type=Date&theme=dark" />
   <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=seyhunak/twitter-bootstrap-rails&type=Date" />
   <img alt="Star history of seyhunak/twitter-bootstrap-rails over time" src="https://api.star-history.com/svg?repos=seyhunak/twitter-bootstrap-rails&type=Date" />
 </picture>
</a>


## License

MIT. Copyright (c) 2025 (since 2011) by Seyhun Akyürek

---

### Superpowers Specs 2026 08 02 Bootstrap 53 Generators Design (`docs/superpowers/specs/2026-08-02-bootstrap-53-generators-design.md`)

# Bootstrap 5.3.8 adaptation and Introduction-page snippet generators

Date: 2026-08-02
Status: Approved

## Goal

Bring `twitter-bootstrap-rails` up to Bootstrap 5.3.8, and make every code snippet
on <https://getbootstrap.com/docs/5.3/getting-started/introduction/> reproducible
through a `rails g bootstrap:*` command.

## Background

The gem is currently inconsistent and partly broken:

- `app/assets/stylesheets/twitter-bootstrap-static/bootstrap.css` vendors Bootstrap
  **5.3.3**; the generated layout CDN-pins **5.3.3** separately. Two places to bump.
- ~~`InstallGenerator#copy_bootstrap_assets` globs
  `vendor/assets/stylesheets/twitter/bootstrap` and
  `vendor/assets/javascripts/twitter/bootstrap`. **Neither path exists.** `Dir.glob`
  returns `[]`, so the method creates two empty directories in the host app and
  copies nothing, silently.~~

  **Correction (verified during implementation):** both paths exist and are
  git-tracked, holding Bootstrap 5.3.3. The glob works. The real defects in this
  area turned out to be:

  - `vendor/assets/stylesheets/twitter-bootstrap-static/bootstrap.css.erb` is
    **Bootstrap 3.1.1** and occupies the same logical asset path as the Bootstrap 5
    `app/assets/stylesheets/twitter-bootstrap-static/bootstrap.css` that
    `bootstrap_and_overrides.css` requires. Whichever the pipeline resolves first wins.
  - `add_assets` calls `File.exist?` on paths relative to the working directory
    rather than the destination root, so an existing `application.js` /
    `application.css` is never detected and gets overwritten by the gem's template.
  - On Propshaft apps there is no `require_self` anchor, so the require insertion
    silently does nothing.
- `vendor/toolkit/**/*.less` and `vendor/static-source/*.less` are the Bootstrap **3**
  Less source. Bootstrap 5 has no Less build. The gemspec still hard-depends on
  `less-rails ~> 4.0` and `execjs ~> 2.7`, and ships a `post_install_message` telling
  users to install a JavaScript runtime for Less compilation.
- `app/assets/fonts/glyphicons-halflings-regular.*` and `app/helpers/glyph_helper.rb`
  support Glyphicons, which Bootstrap dropped in v4.

## Scope

In scope: the five snippets on the Introduction page only.

1. CDN CSS `<link>`
2. CDN JS bundle `<script>`
3. The starter HTML template
4. Popper + `bootstrap.js` as separate `<script>` tags
5. "Important globals" — HTML5 doctype, `<html lang>`, viewport meta

Out of scope: component snippets from the rest of the Bootstrap docs (alerts, cards,
modals, ...). Those may become partials in a later pass.

## Design

### 1. Single source of truth for upstream coordinates

`lib/twitter/bootstrap/rails/version.rb` carries the Bootstrap version and the CDN
URLs with their integrity hashes. No template hardcodes either.

```ruby
module Twitter
  module Bootstrap
    module Rails
      VERSION = "5.4.0"
      BOOTSTRAP_VERSION = "5.3.8"
      POPPER_VERSION = "2.11.8"

      BOOTSTRAP_CDN = {
        css: {
          url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css",
          integrity: "sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
        },
        bundle: {
          url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js",
          integrity: "sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
        },
        js: {
          url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js",
          integrity: "sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y"
        },
        popper: {
          url: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js",
          integrity: "sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
        }
      }.freeze
    end
  end
end
```

Bumping Bootstrap becomes a one-file change plus re-vendoring the two dist files.

A shared module `Bootstrap::Generators::CdnTags` renders tag strings from this hash
and is mixed into the generators that need them. It exposes `css_tag`,
`bundle_tag`, `popper_tag`, and `js_tag`, each returning the exact markup from the
docs page.

### 2. Snippet-to-command mapping

| Docs snippet | Command |
|---|---|
| Starter template | `rails g bootstrap:starter` |
| Important globals | baked into every layout and starter template |
| CDN CSS link | `rails g bootstrap:layout --cdn` |
| CDN JS bundle | `rails g bootstrap:layout --cdn` |
| Popper + bootstrap.js separately | `rails g bootstrap:layout --cdn --separate-popper` |
| Any of the above, as text | `rails g bootstrap:cdn` |

### 3. `bootstrap:install`

Signature: `rails g bootstrap:install [static|cdn]`, default `static`.

- `static` (default) vendors Bootstrap 5.3.8 into the host app's asset pipeline and
  adds the `require` lines to the CSS/JS manifests.
- `cdn` skips vendoring entirely and writes `config/initializers/bootstrap.rb`
  containing `Twitter::Bootstrap::Rails.asset_mode = :cdn`. `bootstrap:layout` reads
  `Twitter::Bootstrap::Rails.asset_mode` to pick its default; passing `--cdn` or
  `--no-cdn` overrides it. The accessor is defined in
  `lib/twitter/bootstrap/rails/bootstrap.rb` as an `mattr_accessor` defaulting to
  `:static`, so the layout generator works whether or not the initializer exists.

Behaviour changes:

- `copy_bootstrap_assets` is fixed to read from paths that exist. Bootstrap 5.3.8's
  `bootstrap.min.css` and `bootstrap.bundle.min.js` are downloaded from jsDelivr,
  verified against the integrity hashes above, and committed to
  `vendor/assets/stylesheets/twitter-bootstrap-static/` and
  `vendor/assets/javascripts/twitter/bootstrap/` respectively. The generator raises
  a clear error if a source file is missing rather than creating empty directories.
- The `less` argument value, the `bootstrap_and_overrides.less` template, the
  `--no-coffeescript` option, and the `bootstrap.coffee` template are removed.

### 4. `bootstrap:layout`

Gains two options:

- `--cdn` / `--no-cdn` — emit the jsDelivr `<link>`/`<script>` tags with integrity
  and `crossorigin` attributes, instead of `stylesheet_link_tag` /
  `javascript_include_tag`. Default comes from the mode recorded by
  `bootstrap:install`, falling back to vendored.
- `--separate-popper` — with `--cdn`, emit the Popper + `bootstrap.min.js` pair
  instead of the single bundle. Ignored with a warning when `--cdn` is not set,
  since the vendored path ships the bundle.

All three template engines (erb, haml, slim) support both options.

### 5. `bootstrap:starter` (new)

Writes the Introduction page's starter template to `public/bootstrap-starter.html`
— a standalone HTML document with no ERB, so `public/` is where it can be opened
directly in a browser. `--path=PATH` overrides the destination. The file is the docs
snippet verbatim, with the version and integrity values interpolated from the
constants.

### 6. `bootstrap:cdn` (new)

Prints tags to stdout rather than writing a file, for pasting into a layout the user
already owns. Writes nothing to disk.

- `rails g bootstrap:cdn` — CSS link + JS bundle script
- `rails g bootstrap:cdn --css` — CSS link only
- `rails g bootstrap:cdn --js` — JS bundle script only
- `rails g bootstrap:cdn --separate-popper` — Popper + bootstrap.js pair

### 7. Removals

Deleted outright, since nothing in Bootstrap 5 uses them:

- `vendor/toolkit/` (Bootstrap 3 Less source)
- `vendor/static-source/` (Bootstrap 3 Less source)
- `app/assets/fonts/glyphicons-halflings-regular.*`
- `app/helpers/glyph_helper.rb` and `spec/lib/twitter_bootstrap_rails/glyph_helper_spec.rb`
- `lib/generators/bootstrap/install/templates/bootstrap_and_overrides.less`
- `lib/generators/bootstrap/install/templates/bootstrap.coffee`
- gemspec: the `less-rails` and `execjs` dependencies, and `post_install_message`

`GlyphHelper` is removed rather than deprecated because its icon font is being
deleted in the same pass; leaving the helper would emit markup for glyphs that no
longer resolve.

## Versioning

Gem version goes to **5.4.0**. Dropping Less, Glyphicons, and the `less` install
argument breaks existing users, so this cannot be a patch. The gem version
consequently stops tracking the Bootstrap version; `BOOTSTRAP_VERSION` is the
authoritative statement of which Bootstrap ships.

## Testing

New specs under `spec/lib/generators/`, using `Rails::Generators::TestCase` against
a tmp destination root:

- `install_generator_spec` — `static` vendors both dist files into the destination
  and inserts the manifest `require` lines; `cdn` vendors nothing and writes the
  initializer; a missing vendored source raises rather than silently no-opping.
- `layout_generator_spec` — default emits `stylesheet_link_tag`; `--cdn` emits the
  CSS link and bundle script with the exact URL and integrity from the constants;
  `--cdn --separate-popper` emits the Popper and `bootstrap.min.js` pair and no
  bundle; `--separate-popper` without `--cdn` warns. Repeated per engine.
- `starter_generator_spec` — output is byte-identical to the docs snippet once the
  version and hashes are interpolated; `--path` is honoured.
- `cdn_generator_spec` — each flag prints the expected tags and writes no file.
- A guard spec asserting no template contains a hardcoded `bootstrap@` version
  string, so the constants stay the only source.

Existing helper specs continue to run unchanged, minus the deleted glyph spec.

## Documentation

README: update the generator table with `bootstrap:starter` and `bootstrap:cdn` and
the new `bootstrap:layout` options. CHANGELOG: a 5.4.0 entry with a Breaking Changes
section covering the Less, Glyphicons, coffeescript, and `bootstrap:install less`
removals, and the fixed vendoring bug.

---

--- METRICS ---
- Files Extracted: 2
- Estimated Token Budget: ~4551 tokens
- Canonical Reference: https://codewiki.google/github.com/seyhunak/twitter-bootstrap-rails
