Introduction to MintyFlaskSSG
A Static Site Generator Built with Flask
If you're a Flask developer looking for a static site generator, you've probably encountered Hugo (Go templates), Jekyll (Ruby and Liquid), or Pelican (Python but not Flask-like). Each requires learning new patterns, different template syntax, or working outside the Python ecosystem you know.
MintyFlaskSSG offers a different approach: build static sites using the Flask patterns you already know.
# This is MintyFlaskSSG - it's just Flask
from flask import Blueprint, render_template
from app.extensions import f_pages
blog_bp = Blueprint('blog_bp', __name__)
@blog_bp.route('/articles/<path:slug>')
def post_view(slug: str):
"""Display an individual blog post."""
path = f"posts/{slug}"
post = f_pages.get_or_404(path)
return render_template("pages/post-single.html", page=post)
Preview with flask run. Build static files with flask freeze. Deploy anywhere. It's Flask—just frozen.
What is MintyFlaskSSG?
MintyFlaskSSG is a static site generator built with Flask that embraces Flask's architecture rather than hiding it. It's not a framework on top of Flask—it is Flask, using Flask-FlatPages for Markdown processing and Frozen-Flask for static generation.
Built for Flask developers who want:
- Jinja2 templates they already use
- Flask blueprints for organisation
- Python ecosystem and libraries
- Familiar app factory pattern
- Standard Flask development workflow
What you get:
- Write Flask code with standard patterns
- Preview with Flask's development server
- Generate static files with Frozen-Flask
- Deploy to any static hosting or CDN
The MintyFlaskSSG Approach
MintyFlaskSSG lets you use Flask's familiar development workflow for static site generation:
Develop with Flask:
# Standard Flask app factory
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
# Initialise Flask-FlatPages for Markdown
f_pages.init_app(app)
# Register blueprints as normal
from app.blog import blog_bp
app.register_blueprint(blog_bp)
return app
Preview with Flask:
flask run
# Browse to http://localhost:5000
Build static files:
flask freeze
# Generates complete static site in build/
Deploy anywhere:
Upload the build/ directory to any static hosting—Netlify, Vercel, GitHub Pages, S3, or your own CDN. No Flask runtime required in production.
Key Features
1. Three-Layer Template Architecture
Templates resolve through Flask's ChoiceLoader in priority order:
Theme Layer (themes/your-theme/)
↓ extends/imports
Mixin Layer (mixins/blog/, mixins/photos/)
↓ extends/includes
Core Layer (themes/_core/)
This is pure Jinja2 template loading—no custom code, no magic:
from jinja2 import FileSystemLoader, ChoiceLoader
template_directories = [
"themes/business-theme/templates/", # Your customisations
"mixins/blog/templates/", # Blog functionality
"mixins/photos/templates/", # Photo galleries
"themes/_core/templates/" # Base structure
]
app.jinja_loader = ChoiceLoader([
FileSystemLoader(dir) for dir in template_directories
])
When a template extends base.html, Flask searches these directories in order. Themes override mixins, mixins override core. Standard Jinja2 behaviour with organised search paths.
Example blog post template hierarchy:
- Layout:
themes/business/templates/layouts/post.html(theme customisation) - Components:
mixins/blog/templates/blog-components.html(blog features) - Base:
themes/_core/templates/base.html(foundation)
2. Markdown-First Content
Write content in Markdown with YAML frontmatter:
---
title: "Getting Started with Flask"
date: 2025-11-30
categories: ["tutorials", "flask"]
tags: ["python", "web-development"]
status: "published"
---
# Getting Started with Flask
Flask is a lightweight WSGI web application framework...
CommonMark renderer with GitHub/Discord behaviour:
- Top level item
- Three-space indented subitem (works correctly)
- Another subitem
- Another top level item
MintyFlaskSSG uses a CommonMark-compliant renderer that handles nested lists the way GitHub, Discord, and most modern Markdown editors do—with 3-space indentation support.
3. YAML-Driven Configuration
Configure your site without touching templates:
# data/yaml/site.yaml
site:
name: "My Flask Blog"
tagline: "Thoughts on web development"
author: "Your Name"
i18n:
en:
site_name: "My Flask Blog"
navigation:
- nav-item:
title: "Articles"
url: "/articles/all"
- nav-item:
title: "About"
url: "/about"
nl:
site_name: "Mijn Flask Blog"
navigation:
- nav-item:
title: "Artikelen"
url: "/artikelen/all"
- nav-item:
title: "Over"
url: "/over"
YAML data flows into templates via context processors:
<title>{{ i18n_yaml.site_name }}</title>
{% for nav in navigation %}
{% set item = nav['nav-item'] %}
<a href="{{ item.url }}">{{ item.title }}</a>
{% endfor %}
Multi-language support built-in:
Language-specific content lives in organised directories:
content/posts/en/for English postscontent/posts/nl/for Dutch postscontent/pages/en/for English pages
Routes handle language switching automatically based on URL structure or user preference.
4. Minimal JavaScript Usage
MintyFlaskSSG emphasises server-side rendering and minimises JavaScript dependencies:
Jinja2 template engine handles:
- All page structure and content
- Navigation and routing
- Conditional rendering
- Data formatting and presentation
JavaScript used only where necessary:
- PhotoSwipe for image galleries
- Alpine.js for dark mode toggle
- Optional enhancements (not requirements)
The philosophy: deliver fast, accessible static pages that work without JavaScript, then progressively enhance where it adds value.
5. Static Site Generation
Frozen-Flask converts Flask routes to static HTML:
# app/freeze_site.py
from flask_frozen import Freezer
from app import create_app
def freeze_site():
app = create_app()
app.config['FREEZER_DESTINATION'] = '../build'
freezer = Freezer(app)
# Register URL generators for dynamic routes
@freezer.register_generator
def blog_posts():
for post in f_pages:
if post.path.startswith('posts/'):
slug = post.path.replace('posts/', '')
yield {'slug': slug}
urls = freezer.freeze()
return len(urls)
Environment-based content filtering:
Development mode shows all content (including drafts):
if app.config['ENVIRONMENT'] == 'development':
# Show draft posts
posts = [p for p in f_pages if p.path.startswith('posts/')]
Production build filters unpublished content:
if app.config['ENVIRONMENT'] == 'production':
# Only published posts
posts = [
p for p in f_pages
if p.path.startswith('posts/')
and p.meta.get('status') == 'published'
]
Generated output:
build/
├── index.html
├── articles/
│ ├── all.html
│ ├── getting-started-with-flask.html
│ ├── category/
│ │ └── tutorials.html
│ └── tag/
│ └── python.html
├── about.html
├── static/
│ ├── css/
│ └── js/
└── photos/
└── processed/
Deploy this directory to any static hosting—no Flask runtime required.
Getting Started
Ready to build static sites with Flask patterns you already know?
1. Installation
Install MintyFlaskSSG and dependencies:
pip install -r requirements.txt
See the Installation Guide for detailed setup instructions including Tailwind CSS configuration and optional Pandoc installation.
2. Configuration
Configure your site in YAML files:
# data/yaml/site.yaml
site:
name: "My Flask Blog"
tagline: "Built with MintyFlaskSSG"
author: "Your Name"
See the Configuration Guide for complete configuration options.
3. Create Your First Post
Write content in Markdown:
---
title: "Hello MintyFlaskSSG"
date: 2025-11-30
categories: ["getting-started"]
tags: ["first-post"]
status: "published"
---
This is my first post using MintyFlaskSSG...
See the Content Management Guide for content creation details.
4. Preview Your Site
Run Flask's development server:
flask run
Browse to http://localhost:5000 to see your site with auto-reload enabled.
5. Build Static Files
Generate static HTML:
flask freeze
Your static site is now in the build/ directory, ready for deployment.
6. Deploy
Upload the build/ directory to any static hosting:
- Netlify: Drag and drop
build/folder - Vercel: Connect Git repository
- GitHub Pages: Push to gh-pages branch
- S3 + CloudFront: Sync with AWS CLI
- Any web server: Upload via FTP/SFTP
Philosophy
Flask-Native Architecture
MintyFlaskSSG is Flask. We don't wrap Flask or hide it—we embrace Flask's patterns:
- App factory for configuration and testing
- Blueprints for code organisation
- Context processors for template data
- Flask-FlatPages for content
- Standard Flask development workflow
If you know Flask, you know MintyFlaskSSG. No new framework to learn.
Markdown-First Content
Content lives in version-controlled Markdown files:
content/
├── posts/
│ └── 2025-11-30-first-post.md
└── pages/
└── about.md
Benefits:
- Git-friendly workflow (track changes, pull requests, rollbacks)
- No database required
- Portable across systems
- Human-readable source files
- Standard text editor workflow
Build-Time Processing
Images, content, and assets process during build:
flask freeze # ← All processing happens here
Production output:
- Optimised images with multiple sizes
- Pre-rendered HTML pages
- Minified CSS and JavaScript
- No runtime dependencies
Deploy static files that load instantly. No server-side processing in production.
Separation of Concerns
Clear boundaries between layers:
Content Layer (Markdown + YAML):
---
title: "Article Title"
---
Article content...
Logic Layer (Flask Blueprints):
@blog_bp.route('/articles/<slug>')
def post_view(slug):
return render_template('post.html', post=post)
Presentation Layer (Jinja2 Templates):
<article>
<h1>{{ post.title }}</h1>
{{ post.html|safe }}
</article>
Styling Layer (Tailwind CSS v4):
.card-base {
padding: theme(spacing[6]);
background: var(--color-surface-primary);
}
Each layer has clear responsibilities. Content creators write Markdown. Developers write Flask code. Designers style components. No layer interferes with others.
The Flask Advantage
Building on Flask provides unique advantages:
Future-ready architecture:
Start with static generation, add dynamic features later if needed:
- Static pages for content
- Dynamic Flask endpoints for forms/search
- Hybrid deployment via reverse proxy
The Flask foundation means you're not locked into pure static generation.
Python library ecosystem:
Use any Python library during build:
# Pull data from APIs
import requests
response = requests.get('https://api.example.com/posts')
# Process images
from PIL import Image
img = Image.open('photo.jpg')
# Parse data formats
import yaml
config = yaml.safe_load(file)
Full access to Python's rich ecosystem.
Familiar debugging tools:
Standard Flask debugging works normally:
- Set breakpoints in VS Code or PyCharm
- Use Flask's debug toolbar
- Inspect variables with
pdb - View SQL queries (if using database)
No learning new debugging approaches.
Standard deployment options:
The Flask foundation means you can:
- Deploy static files only (typical use)
- Run as Flask application if needed
- Mix static and dynamic via reverse proxy
- Use Flask-specific hosting services
Flexibility to choose deployment strategy.
Welcome to MintyFlaskSSG
A static site generator built with Flask, using Flask patterns Flask developers already know.
Build static sites with Jinja2 templates, Python libraries, and standard Flask workflows. Preview with flask run. Build with flask freeze. Deploy anywhere.
It's Flask—just frozen.