Skip to main content

Overview

Blueprints are Flask’s way of organizing applications into modular components. They allow you to structure large applications into smaller, reusable pieces.

What are Blueprints?

A blueprint is a template for generating a “section” of a web application. You can think of it as a mini-application that can be registered on the main Flask app.

Why Use Blueprints?

  • Modularity: Organize code by feature or functionality
  • Reusability: Share blueprints across projects
  • Scalability: Large apps become manageable
  • Team Development: Different teams work on different blueprints
  • Namespace: Avoid naming conflicts with prefixes

Creating a Blueprint

From blueprints.py:18-54:
from flask import Blueprint

# Create a blueprint
admin = Blueprint(
    'admin',           # Blueprint name
    __name__,         # Import name
    url_prefix='/admin',
    template_folder='templates',
    static_folder='static'
)

@admin.route('/')
def index():
    return 'Admin Dashboard'

@admin.route('/users')
def users():
    return 'User Management'

Registering Blueprints

Register blueprints with the Flask application:
from flask import Flask
from myapp.admin import admin
from myapp.api import api

app = Flask(__name__)

# Register blueprints
app.register_blueprint(admin)
app.register_blueprint(api, url_prefix='/api')

if __name__ == '__main__':
    app.run()

Blueprint Parameters

Basic Parameters

blueprint = Blueprint(
    name='admin',                    # Blueprint name (required)
    import_name=__name__,           # Module import name (required)
    static_folder='static',         # Static files folder
    static_url_path='/static',      # URL path for static files
    template_folder='templates',    # Templates folder
    url_prefix='/admin',            # URL prefix for all routes
    subdomain='admin',              # Subdomain for blueprint
    url_defaults={'lang': 'en'},    # Default URL values
    root_path=None,                 # Root path override
)

URL Prefix

Add a prefix to all blueprint routes:
api = Blueprint('api', __name__, url_prefix='/api/v1')

@api.route('/users')  # Accessible at /api/v1/users
def list_users():
    return jsonify(users)

# Override at registration
app.register_blueprint(api, url_prefix='/api/v2')

Subdomain

Mount blueprint on a subdomain:
app.config['SERVER_NAME'] = 'example.com'

admin = Blueprint('admin', __name__, subdomain='admin')

@admin.route('/')  # Accessible at admin.example.com/
def admin_home():
    return 'Admin Home'

app.register_blueprint(admin)

Blueprint Structure

myapp/
├── __init__.py          # Application factory
├── models.py           # Database models
├── admin/
│   ├── __init__.py     # Blueprint creation
│   ├── views.py        # Route handlers
│   ├── forms.py        # WTForms
│   ├── templates/
│   │   └── admin/
│   │       ├── index.html
│   │       └── users.html
│   └── static/
│       └── admin.css
├── api/
│   ├── __init__.py
│   ├── users.py
│   └── products.py
└── main/
    ├── __init__.py
    ├── views.py
    └── templates/
        └── main/
            └── index.html

Example Blueprint Module

myapp/admin/__init__.py:
from flask import Blueprint

admin = Blueprint(
    'admin',
    __name__,
    template_folder='templates',
    static_folder='static',
    url_prefix='/admin'
)

from . import views
myapp/admin/views.py:
from flask import render_template, redirect, url_for
from . import admin

@admin.route('/')
def index():
    return render_template('admin/index.html')

@admin.route('/users')
def users():
    users = get_all_users()
    return render_template('admin/users.html', users=users)

Blueprint Routes

Defining Routes

Blueprints use the same routing decorators as the main app:
from flask import Blueprint, request, jsonify

api = Blueprint('api', __name__)

@api.route('/users', methods=['GET', 'POST'])
def users():
    if request.method == 'POST':
        return create_user()
    return list_users()

@api.get('/users/<int:id>')
def get_user(id):
    return jsonify(user)

@api.post('/users')
def create_user():
    return jsonify(user), 201

@api.delete('/users/<int:id>')
def delete_user(id):
    return '', 204

Endpoint Names

Blueprint routes get prefixed endpoint names:
admin = Blueprint('admin', __name__)

@admin.route('/')
def index():  # Endpoint: 'admin.index'
    return 'Admin Home'

# Use in url_for()
url_for('admin.index')  # /admin/
url_for('admin.users')  # /admin/users

Blueprint Resources

Templates

From templating.py:98-107, blueprints can have their own templates:
admin = Blueprint('admin', __name__, template_folder='templates')

@admin.route('/')
def index():
    # Looks in blueprint's templates folder first, then app's
    return render_template('admin/index.html')
Template search order:
  1. Blueprint’s templates folder
  2. Application’s templates folder

Static Files

From blueprints.py:82-102, serve static files from blueprints:
admin = Blueprint(
    'admin',
    __name__,
    static_folder='static',
    static_url_path='/admin/static'
)

# In templates
{{ url_for('admin.static', filename='admin.css') }}
# Generates: /admin/static/admin.css

Resource Files

From blueprints.py:104-129, open blueprint resources:
@admin.route('/init')
def init_database():
    with admin.open_resource('schema.sql') as f:
        db.executescript(f.read())
    return 'Database initialized'

Blueprint Hooks

Before Request

@admin.before_request
def check_admin():
    if not current_user.is_admin:
        abort(403)

@admin.before_app_request  # Runs for ALL requests
def log_request():
    app.logger.info(f'{request.method} {request.path}')

After Request

@admin.after_request
def add_header(response):
    response.headers['X-Admin'] = 'true'
    return response

@admin.after_app_request  # Runs for ALL requests
def add_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

Teardown

@admin.teardown_request
def cleanup(exception=None):
    # Clean up blueprint resources
    pass

@admin.teardown_app_request  # Runs for ALL requests
def global_cleanup(exception=None):
    db.session.remove()

Error Handlers

@admin.errorhandler(404)
def admin_not_found(error):
    return render_template('admin/404.html'), 404

@admin.app_errorhandler(500)  # Handles ALL 500 errors
def server_error(error):
    return render_template('errors/500.html'), 500

Context Processors

Add variables to blueprint templates:
@admin.context_processor
def inject_admin_context():
    return dict(admin_version='1.0')

@admin.app_context_processor  # For ALL templates
def inject_global():
    return dict(site_name='My Site')

Nested Blueprints

Blueprints can be nested (Flask 2.0+):
parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')

@child.route('/')
def index():
    return 'Child Index'

# Register child on parent
parent.register_blueprint(child)

# Register parent on app
app.register_blueprint(parent)

# Routes to: /parent/child/

Blueprint Factories

Create configurable blueprints:
def create_api_blueprint(version='v1'):
    api = Blueprint(
        f'api_{version}',
        __name__,
        url_prefix=f'/api/{version}'
    )
    
    @api.route('/users')
    def users():
        return jsonify(users)
    
    return api

# Register multiple versions
app.register_blueprint(create_api_blueprint('v1'))
app.register_blueprint(create_api_blueprint('v2'))

URL Generation

Within Blueprint

@admin.route('/')
def index():
    # Relative to current blueprint
    url_for('.users')  # Same as url_for('admin.users')
    
    # Absolute
    url_for('admin.users')
    
    # Other blueprint
    url_for('api.users')
    
    # Main app
    url_for('index')

From Templates

{# Current blueprint #}
<a href="{{ url_for('.users') }}">Users</a>

{# Specific blueprint #}
<a href="{{ url_for('admin.users') }}">Admin Users</a>

{# Static files #}
<link rel="stylesheet" href="{{ url_for('.static', filename='style.css') }}">

CLI Commands

Add CLI commands to blueprints:
import click
from flask import Blueprint

admin = Blueprint('admin', __name__)

@admin.cli.command('init-db')
def init_db():
    """Initialize the admin database."""
    click.echo('Initializing admin database...')
    # Initialization code

# Usage: flask admin init-db

Application Factory Pattern

Combine blueprints with application factories:
def create_app(config_name='default'):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    
    # Initialize extensions
    db.init_app(app)
    login_manager.init_app(app)
    
    # Register blueprints
    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)
    
    from .admin import admin as admin_blueprint
    app.register_blueprint(admin_blueprint, url_prefix='/admin')
    
    from .api import api as api_blueprint
    app.register_blueprint(api_blueprint, url_prefix='/api/v1')
    
    return app

Best Practices

Organize by Feature

Group related functionality together, not by type

Clear Naming

Use descriptive blueprint names that reflect their purpose

Avoid Circular Imports

Import views at the end of init.py

Use Factories

Combine with application factory pattern for flexibility

Common Patterns

API Blueprint

api = Blueprint('api', __name__, url_prefix='/api/v1')

@api.before_request
def verify_token():
    token = request.headers.get('Authorization')
    if not verify_api_token(token):
        abort(401)

@api.after_request
def add_cors_headers(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    return response

Auth Blueprint

auth = Blueprint('auth', __name__)

@auth.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # Process login
        return redirect(url_for('main.index'))
    return render_template('auth/login.html')

@auth.route('/logout')
def logout():
    # Clear session
    return redirect(url_for('main.index'))

Build docs developers (and LLMs) love