Bootstrap 4 & 5 helper for Flask projects providing Jinja macros for forms, tables, navigation, and utilities.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pagination component rendering for Flask-SQLAlchemy pagination objects with customizable navigation controls, alignment options, URL fragment support, and responsive design considerations.
Render complete pagination with numbered pages and navigation controls.
def render_pagination(pagination, endpoint=None, prev='«', next='»',
size=None, ellipses='…', args={}, fragment='', align=''):
"""
Render full pagination with numbered pages.
Args:
pagination: Flask-SQLAlchemy Pagination object
endpoint (str): Flask endpoint for pagination links (defaults to current endpoint)
prev (str): Previous button text/HTML (default: '«')
next (str): Next button text/HTML (default: '»')
size (str): Pagination size - 'sm' or 'lg' for small/large
ellipses (str): Text for ellipsis indicator (default: '…')
args (dict): Additional URL parameters to preserve
fragment (str): URL fragment (#section) to append to links
align (str): Alignment - 'center' or 'right' (default: left)
Returns:
Rendered HTML pagination with Bootstrap styling
Pagination Object Properties Used:
- page: Current page number
- pages: Total number of pages
- per_page: Items per page
- total: Total number of items
- has_prev: Boolean for previous page availability
- has_next: Boolean for next page availability
- prev_num: Previous page number
- next_num: Next page number
- iter_pages(): Iterator for page numbers with gaps
"""Render simplified pagination with only Previous/Next buttons.
def render_pager(pagination, fragment='',
prev='<span aria-hidden="true">←</span> Previous',
next='Next <span aria-hidden="true">→</span>', align=''):
"""
Render simple pagination pager (Previous/Next only).
Args:
pagination: Flask-SQLAlchemy Pagination object
fragment (str): URL fragment to append to links
prev (str): Previous button text/HTML
next (str): Next button text/HTML
align (str): Alignment - 'center' or 'right'
Returns:
Rendered HTML pager with Bootstrap styling
Usage:
- Simpler alternative to full pagination
- Better for mobile interfaces
- Suitable when page numbers aren't necessary
"""Internal helper for rendering the current active page indicator.
def get_current_page(page):
"""
Render the current active page in pagination.
Args:
page (int): Current page number
Returns:
Rendered HTML for current page indicator
Note: Internal helper used by pagination macros
"""Bootstrap-Flask pagination works seamlessly with Flask-SQLAlchemy's pagination system:
# View function with pagination
@app.route('/posts')
def posts():
page = request.args.get('page', 1, type=int)
posts = Post.query.paginate(
page=page,
per_page=5,
error_out=False
)
return render_template('posts.html', posts=posts)Pagination automatically preserves existing URL parameters:
# URL: /search?q=python&category=tutorial&page=2
args = {'q': 'python', 'category': 'tutorial'}Pagination components adapt to different screen sizes:
# View function
@app.route('/users')
def users():
page = request.args.get('page', 1, type=int)
users = User.query.paginate(
page=page,
per_page=10,
error_out=False
)
return render_template('users.html', users=users)<!-- Template -->
{% from 'base/pagination.html' import render_pagination %}
<div class="container">
<!-- Display items -->
{% for user in users.items %}
<div class="card mb-2">
<div class="card-body">
<h5>{{ user.name }}</h5>
<p>{{ user.email }}</p>
</div>
</div>
{% endfor %}
<!-- Pagination -->
{{ render_pagination(users) }}
</div>{{ render_pagination(posts, align='center') }}{{ render_pagination(products, align='right') }}{{ render_pagination(posts, size='lg') }}{{ render_pagination(comments, size='sm') }}{{ render_pagination(posts,
prev='← Previous Posts',
next='Next Posts →'
) }}# View preserving search parameters
@app.route('/search')
def search():
query = request.args.get('q', '')
category = request.args.get('category', '')
page = request.args.get('page', 1, type=int)
results = Post.query.filter(
Post.title.contains(query)
).paginate(page=page, per_page=10)
return render_template('search.html',
results=results,
search_args={'q': query, 'category': category}){{ render_pagination(results, args=search_args) }}<!-- Jump to results section after page change -->
{{ render_pagination(results, fragment='results') }}
<!-- Results section -->
<div id="results">
<!-- Search results here -->
</div>{% from 'base/pagination.html' import render_pager %}
<!-- Simple Previous/Next pager -->
{{ render_pager(posts) }}
<!-- Centered pager -->
{{ render_pager(posts, align='center') }}
<!-- Custom pager text -->
{{ render_pager(posts,
prev='← Older Posts',
next='Newer Posts →'
) }}<div class="d-flex justify-content-between align-items-center mb-3">
<small class="text-muted">
Showing {{ posts.per_page * (posts.page - 1) + 1 }} to
{{ posts.per_page * posts.page if posts.page < posts.pages else posts.total }}
of {{ posts.total }} posts
</small>
{{ render_pagination(posts, align='right') }}
</div><!-- Pagination with data attributes for AJAX -->
<div id="pagination-container">
{{ render_pagination(posts, endpoint='api.posts') }}
</div>
<script>
// Handle pagination clicks with AJAX
$('#pagination-container').on('click', '.page-link', function(e) {
e.preventDefault();
const url = $(this).attr('href');
$.get(url, function(data) {
$('#content').html(data.content);
$('#pagination-container').html(data.pagination);
});
});
</script>{% if posts.pages > 1 %}
<nav aria-label="Posts pagination">
{{ render_pagination(posts) }}
</nav>
{% endif %}The pagination macros work identically across Bootstrap versions, but the underlying CSS classes differ:
page-item and page-link classesactive classdisabled class{{ render_pagination(posts,
endpoint='blog.posts', # Custom endpoint
prev='‹ Previous', # Custom previous text
next='Next ›', # Custom next text
size='lg', # Large pagination
ellipses='...', # Custom ellipsis
align='center', # Center alignment
args={'category': category}, # Preserve URL params
fragment='posts' # Jump to section
) }}<div class="row align-items-center">
<div class="col-md-6">
<small class="text-muted">
Page {{ posts.page }} of {{ posts.pages }}
({{ posts.total }} total {{ 'post' if posts.total == 1 else 'posts' }})
</small>
</div>
<div class="col-md-6">
{{ render_pagination(posts, align='right') }}
</div>
</div><div class="d-flex justify-content-between align-items-center mb-3">
<div>
<label class="form-label">Items per page:</label>
<select class="form-select d-inline w-auto" onchange="changePerPage(this.value)">
<option value="10" {{ 'selected' if posts.per_page == 10 }}>10</option>
<option value="25" {{ 'selected' if posts.per_page == 25 }}>25</option>
<option value="50" {{ 'selected' if posts.per_page == 50 }}>50</option>
</select>
</div>
{{ render_pagination(posts) }}
</div>{% if posts.items %}
<!-- Display items -->
{% for post in posts.items %}
<!-- Post content -->
{% endfor %}
<!-- Show pagination only if there are multiple pages -->
{% if posts.pages > 1 %}
{{ render_pagination(posts) }}
{% endif %}
{% else %}
<div class="text-center py-5">
<h4>No posts found</h4>
<p class="text-muted">Try adjusting your search criteria.</p>
</div>
{% endif %}Pagination respects Flask-SQLAlchemy pagination configuration:
# Flask-SQLAlchemy configuration
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Pagination defaults
DEFAULT_PER_PAGE = 20
MAX_PER_PAGE = 100
# In view functions
per_page = min(request.args.get('per_page', DEFAULT_PER_PAGE, type=int), MAX_PER_PAGE)Install with Tessl CLI
npx tessl i tessl/pypi-bootstrap-flask