Flask Project Structure: Blueprints, Templates, and Config
One app.py file feels practical while it contains only a handful of routes. The problem arrives gradually: page routes become mixed with login, config, models, and API endpoints. A file that was once easy to understand starts requiring a long scroll just to find one function.
Blueprints group routes by application area. A Blueprint is not a second Flask application and does not run on its own. It collects routes and is then registered with the main application object.
A Small Structure That Stays Readable
We will separate the notes routes without introducing too many layers:
# File: folder-structure.txt
flask-notes/
├── app.py
├── config.py
├── routes/
│ ├── __init__.py
│ └── notes.py
├── templates/
│ └── notes/
│ └── index.html
└── static/
└── css/
└── app.css
__init__.py may be empty. Its presence clearly marks routes as a Python package, allowing routes.notes to be imported.
Create a Notes Blueprint
Place every note-related route in routes/notes.py:
# File: routes/notes.py
from flask import Blueprint, render_template
notes_bp = Blueprint("notes", __name__, url_prefix="/notes")
@notes_bp.get("")
def index():
notes = ["Meeting notes", "Book ideas"]
return render_template("notes/index.html", notes=notes)
The first argument, "notes", is the Blueprint name. It becomes an endpoint namespace. Because the route function is named index, its full endpoint is notes.index. Templates can later refer to it with url_for("notes.index").
The url_prefix="/notes" value adds the same prefix to every route in this Blueprint. The empty path in @notes_bp.get("") becomes /notes. A future /<int:note_id> route becomes /notes/<int:note_id>.
Assemble the App with create_app()
The main file creates Flask and registers the Blueprint:
# File: app.py
from flask import Flask
from routes.notes import notes_bp
def create_app():
app = Flask(__name__)
app.config.from_object("config.Config")
app.register_blueprint(notes_bp)
return app
Basic configuration can live in its own file:
# File: config.py
class Config:
SECRET_KEY = "dev-secret"
The create_app() function is called an application factory because it creates and returns an application instance. This pattern is useful when tests need different configuration or when several app instances must exist in one process.
Flask can discover the factory automatically:
# File: terminal
python -m flask --app app run --debug
Link a Template to a Blueprint Endpoint
Because the endpoint is namespaced under notes, links use notes.index:
<!-- File: templates/notes/index.html -->
<h1>Notes</h1>
<ul>
{% for note in notes %}
<li>{{ note }}</li>
{% endfor %}
</ul>
<a href="{{ url_for('notes.index') }}">Reload notes</a>
The namespace prevents collisions when another Blueprint also has a function named index. Authentication can expose auth.index while notes keeps notes.index.
Do Not Split the Project Too Early
Blueprints solve route grouping, but that does not mean every function deserves a separate file. Too many folders in a small project can make the flow harder to follow. Split code after clear groups emerge, such as notes, auth, and api.
Watch for circular imports as well. Routes may import models or extensions, but they should avoid importing the app object from the main file. A database extension can be created separately and attached through init_app() inside the factory.
As an exercise, add routes/auth.py with an auth Blueprint, a /login route, and its own template. After registering it in create_app(), inspect the route table with:
# File: terminal
python -m flask --app app routes
The output lists registered endpoints, methods, and URLs. It is a quick way to find a Blueprint that was never registered or a prefix that differs from what you expected.
Related Articles
Keep reading within the same topic.
Flask Authentication: Login, Sessions, and Logout
Build Flask authentication with password hashes, login sessions, a current user, protected routes, and logout without storing plaintext passwords.
Flask CRUD with SQLAlchemy: Create, Read, Update, Delete
Build Flask and SQLAlchemy CRUD to create, read, update, and delete notes with validation, forms, and redirects.
Flask SQLAlchemy: Database Connection and Models
Connect Flask to SQLite with Flask-SQLAlchemy, create models and tables, then understand add, commit, and querying data.
Flask Static Files: CSS, JavaScript, and Assets
Organize Flask static files for CSS, JavaScript, and images, then reference each asset with url_for so paths remain maintainable.