# Bootstrap-Flask: Jinja macros that turn Flask objects into Bootstrap markup

> A Flask extension that renders WTForms, data objects and Flask-SQLAlchemy pagination into Bootstrap 4 or Bootstrap 5 markup through Jinja macros, so a form costs one template line instead of a page of HTML. The awkward part is the two-major-version split and the fact that the macros live in a deprecated lineage borrowed from Flask-Bootstrap.

**helloflask/bootstrap-flask** — Bootstrap 4 & 5 helper for your Flask projects.

- Repository: https://github.com/helloflask/bootstrap-flask
- Website: https://bootstrap-flask.readthedocs.io
- Stars: 1,195 · Forks: 190
- Language: SCSS
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/helloflask-bootstrap-flask

## A form is one template line

Bootstrap-Flask is a collection of Jinja macros for Bootstrap 4 and 5 and Flask. The README describes its purpose as helping you render Flask-related data and objects to Bootstrap markup HTML more easily, and lists three examples of what that means: rendering a Flask-WTF or WTForms form object into a Bootstrap form, rendering data objects, whether dicts or class instances, into a Bootstrap table, and rendering a Flask-SQLAlchemy `Pagination` object into Bootstrap pagination.

The form case is the one the README demonstrates. You register the extension:

```python
from flask import Flask
# To follow the naming rule of Flask extension, although
# this project's name is Bootstrap-Flask, the actual package
# installed is named `flask_bootstrap`.
from flask_bootstrap import Bootstrap5

app = Flask(__name__)
bootstrap = Bootstrap5(app)
```

And in the template, a single macro call renders the whole form:

```html
{% from 'bootstrap5/form.html' import render_form %}
<html>
<head>
<!-- Bootstrap CSS -->
</head>
<body>

<h2>Login</h2>
{{ render_form(form) }}

<!-- Bootstrap JS -->
</body>
</html>
```

The comment in the Python example is worth pausing on, because it is a genuine trap for a new user. The project is called Bootstrap-Flask but the importable package is `flask_bootstrap`, because Flask extensions follow the naming rule of using the `flask_` prefix. Searching for `bootstrap_flask` in your editor will find nothing.

The macro name and the template path both follow the same rule, with the import path carrying the Bootstrap major version.

## Two extension classes, two template paths

Bootstrap 5 support was added in Bootstrap-Flask 2.0, and the consequence is that the extension now has a separate class per Bootstrap major version, with a matching template directory.

For Bootstrap 4 you use the `Bootstrap4` class and import macros from `bootstrap4/`:

```python
from flask_bootstrap import Bootstrap4

# ...
bootstrap = Bootstrap4(app)
```

```html
{% from 'bootstrap4/form.html' import render_form %}
```

For Bootstrap 5 it is the `Bootstrap5` class and the `bootstrap5/` path:

```python
from flask_bootstrap import Bootstrap5

# ...
bootstrap = Bootstrap5(app)
```

```html
{% from 'bootstrap5/form.html' import render_form %}
```

The old `Bootstrap` class and the `bootstrap/` template path are deprecated since 2.0 and will be removed in 3.0. That deprecation is the sharpest edge in the project for an existing user: a template that imports from `bootstrap/form.html` will keep working until the next major version and then stop, and the fix is a mechanical path change, but only if you know it is coming.

The design reason for the split is not merely cosmetic. A Bootstrap 4 and a Bootstrap 5 form differ in markup, in the way validation states are expressed, and in the grid and utility classes, so one set of macros cannot produce correct markup for both. Splitting by major version lets each set be right, at the cost of two import paths to remember.

The repository reflects the split too. The `examples/` directory holds separate `bootstrap4/` and `bootstrap5/` application directories, plus `list_bootswatch.py` and `update_icons.py`, which are the scripts that keep the icon set in step with the Bootstrap releases.

## What the macros actually cover

The README lists the render targets in three bullets: a Flask-WTF or WTForms form object rendered as a Bootstrap form, data objects, dict or class objects, rendered as a Bootstrap table, and a Flask-SQLAlchemy `Pagination` object rendered as Bootstrap pagination.

Each of those is a substantial piece of markup that would otherwise be hand-written. A WTForms field carries its label, its widget, its validators and its error state, and turning that into Bootstrap markup means mapping each field onto the right container, input group and feedback classes, and choosing the right markup for the field type. The pagination object carries a page, a total and a set of links, and rendering it means producing the pagination component with the right disabled and active states.

The form example in the README shows the payoff, and it shows the part that is easy to forget. The form is rendered with validation errors styled correctly, which the README demonstrates with a second screenshot. Without a macro layer, error rendering is exactly where hand-written forms get inconsistent, because the error markup has to be right for every field type and every validator.

The README also points at the Basic Usage documentation for more detail, and there is a live demo hosted at bootstrap-flask-example.azurewebsites.net, plus an example application in the repository's `examples/` directory, which is where you would look to see the table and pagination macros in use.

## A small dependency list and a wide Python range

The `pyproject.toml` declares two runtime dependencies, `Flask` and `WTForms`, and nothing else. There is no dependency on Flask-SQLAlchemy even though the pagination macro targets its `Pagination` object, which means the macro works on any object with the right shape and the extension does not force Flask-SQLAlchemy on you.

The Python classifiers list 3.11, 3.12, 3.13 and 3.14, and the project is classified as Development Status 5, Production/Stable, Environment Web Environment, Intended Audience Developers and Operating System OS Independent.

The build backend is setuptools with a requirement of 61.0 or newer, and the package version is 2.6.0, which matches the most recent release, 2.6.0 on 2026-08-23. The release before it was 2.5.0 on 2025-05-02 and 2.4.2 on 2025-03-09, so the gap between 2.5.0 and 2.6.0 was over a year.

The testing setup is in `pyproject.toml` as well. Pytest is configured with `testpaths = ["tests"]`, and coverage is measured against the `flask_bootstrap` package, with the report excluding lines marked `pragma: no cover` and the `except ImportError:` clause, which is the standard way to handle optional dependencies in a coverage report. The repository also carries a `tox.ini`, a `requirements/` directory, a `.flake8` configuration and a `codecov.yml`.

The `flask_bootstrap/` package directory is where the implementation lives, and the `docs/` directory is the source for the readthedocs site.

## The licence is MIT with BSD leftovers from Flask-Bootstrap

The licence situation needs reading twice, because the GitHub metadata records it as a custom licence that cannot be classified, while the `pyproject.toml` says MIT.

The README explains the reason. The project is licensed under the MIT License, and some macros were part of Flask-Bootstrap and were modified under the terms of its BSD License. So the repository contains MIT-licensed code and BSD-licensed code that was inherited and modified, and the combined work is published with a licence file that GitHub cannot map to a single identifier.

That is not a problem in itself. MIT and BSD are both permissive, they require the notice to be retained, and neither obliges you to open-source your own application. The practical point is that the `LICENSE` file is the authoritative document and the SPDX field in the packaging metadata is a simplification.

The project is by Grey Li, and it is funded through Open Collective, with the README asking for donations and stating that the donation keeps Bootstrap-Flask maintained and updated with Bootstrap. The funding page is linked in the project URLs as well.

The relationship with the older Flask-Bootstrap is worth noting because it explains the whole shape of the project. This extension is the successor to Flask-Bootstrap, and the README links a migration tutorial for people coming from it. The macros, the template structure and the deprecated `bootstrap/` path all descend from that lineage, which is why the licence is a mixture and why the upgrade path from Flask-Bootstrap is documented as a tutorial rather than a note in a changelog.

## Where Bootstrap-Flask is the wrong choice

The first wrong case is a project on Bootstrap 3 or earlier, or one that has not chosen a CSS framework at all. The extension covers Bootstrap 4 and 5, and if your organisation standardised on something else then the value of the macros is mostly the form-error handling, which you could write yourself.

The second is a team that wants to own its markup. The macros decide the DOM structure, the class names and the error presentation. Overriding them means writing your own Jinja templates, and at that point you are maintaining a copy of the macros. The extension is a good fit when you accept its markup, and a bad fit when a designer wants a different structure for one form.

The third is an application that needs accessibility behaviours the macros do not cover. The README does not describe ARIA attributes, focus management on validation failure, or screen reader announcements, and a login form that fails validation has to tell a screen reader user what went wrong. Whether the macros handle it is not something the documentation answers, so it is worth checking against your accessibility requirements before you standardise on them.

The fourth is a codebase that still uses the deprecated paths and plans to upgrade to 3.0. Templates importing from `bootstrap/form.html` will break on the next major release. The fix is small, but it is work you have to schedule rather than something you can ignore indefinitely.

The fifth is a team that has already outgrown Jinja macros for forms. At a certain complexity, a form that needs dynamic fields, dependent validation, custom widgets and optimistic updates is no longer a static template, and the macro model starts fighting the requirement. The `docs/` and `examples/` directories are the place to see how far the model stretches.

## The version split is the thing to plan around

Putting the pieces together, the practical shape of Bootstrap-Flask is a small, stable, well-funded extension with one upcoming breaking change.

The repository is not archived and the last push was on 2026-10-13, with 2.6.0 released on 2026-08-23. The extension has two runtime dependencies, a version that is classified as production stable, a Python range that spans 3.11 to 3.14, and a live demo plus example applications for both Bootstrap major versions.

The one thing with a date on it is the deprecation. The `Bootstrap` class and the `bootstrap/` template path are deprecated since 2.0 and will be removed in 3.0, so a project that adopts the current release is buying a migration to perform later, and a project that is already on the old paths has that migration pending now.

The related question is which Bootstrap major version to standardise on, because the extension class and the template path have to match, and they are chosen in two different places. The extension class is set once in Python, the template path is set per template. A project that mixes `bootstrap4/form.html` and `bootstrap5/form.html` imports will render two different markup dialects, and nothing in the extension stops that.

The `CHANGES.rst` file in the repository root is the version-by-version record, and the discussions page linked from the README is where the roadmap for the next release is argued out.

## Conclusion

Bootstrap-Flask suits a Flask team that has decided on Bootstrap and wants its forms, tables and pagination to look consistent without hand-writing markup for every field, error message and page link. It does not suit a project on Bootstrap 3 or earlier, since support covers Bootstrap 4 and 5 only, and it does not suit a team that wants full control of the rendered HTML, because the macros decide the structure and you override it by writing your own templates. Before adopting it, pick the extension class that matches your Bootstrap major version and import the macros from the matching `bootstrap4/` or `bootstrap5/` template path, since the `Bootstrap` class and the `bootstrap/` path are deprecated since 2.0 and scheduled for removal in 3.0, and read the `LICENSE` file, which covers both the MIT terms of this project and BSD terms inherited from Flask-Bootstrap macros.

## FAQ

### How do I install and set up Bootstrap-Flask?

Install it with `pip install -U bootstrap-flask`, then create the extension in your application with `bootstrap = Bootstrap5(app)` for Bootstrap 5 or `bootstrap = Bootstrap4(app)` for Bootstrap 4, importing from `flask_bootstrap`.

### Why is the import `flask_bootstrap` and not `bootstrap_flask`?

The README explains it: to follow the naming rule of Flask extensions, although the project is named Bootstrap-Flask, the actual package installed is `flask_bootstrap`.

### How do I render a WTForms form with Bootstrap markup?

Import the `render_form` macro from the template path for your Bootstrap version, `bootstrap5/form.html` or `bootstrap4/form.html`, and call it with the form object. The README notes the error messages are rendered with proper styling when validation fails.

### What is deprecated in Bootstrap-Flask 2.6?

The `Bootstrap` class and the `bootstrap/` template path are deprecated since version 2.0 and will be removed in 3.0. You should use the `Bootstrap4` or `Bootstrap5` class and import macros from the matching `bootstrap4/` or `bootstrap5/` path.

### What licence is Bootstrap-Flask under?

The project is licensed under the MIT License, and some macros were part of Flask-Bootstrap and were modified under the terms of its BSD License. The `LICENSE` file in the repository root covers both.

## Sources

- [helloflask/bootstrap-flask on GitHub](https://github.com/helloflask/bootstrap-flask)
- [Issues](https://github.com/helloflask/bootstrap-flask/issues)
- [Project website](https://bootstrap-flask.readthedocs.io)
- [README](https://github.com/helloflask/bootstrap-flask/blob/main/README.md)
- [Releases](https://github.com/helloflask/bootstrap-flask/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/helloflask-bootstrap-flask
