Python Flask Tutorial: Full-Featured Web App Part 2 - Templates
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Create a templates/ directory and move full HTML documents into home.html and about.html instead of returning raw HTML strings from routes.
Briefing
Flask templates turn messy, repeated HTML strings into maintainable web pages—and the biggest upgrade comes from template inheritance, which lets one shared “layout” control the look of every route. Instead of returning raw HTML from route functions, the app creates a templates/ directory with home.html and about.html. Each route then uses Flask’s render_template to pull in a full HTML document structure from disk, so the browser receives complete pages (doctype, html, head, body) rather than a single heading tag.
Once templates are in place, the workflow shifts from hardcoded markup to dynamic rendering. Dummy blog-post data is defined as a list of dictionaries (author, title, content, date posted) and passed into templates via render_template. Inside the template, Jinja—Flask’s templating engine—iterates over that list using for loops and prints fields with double-curly-brace expressions like {{ post.title }} and {{ post.author }}. The result is server-side HTML generation: the page source shows the loop expanded into repeated sections for each post, not placeholders.
Templates also support conditional logic. A title variable can be provided to a template; if it exists, the page title becomes “Flask Blog - <title>,” and if it doesn’t, the template falls back to a default. This conditional behavior is used across both home and about pages: home relies on the default, while about passes title=about to produce a distinct browser tab title.
The major maintainability win arrives when home.html and about.html share lots of identical head and layout code. Rather than duplicating that repeated structure in every template, a new layout.html acts as the parent template. It contains the common HTML skeleton and a named block (block content) that child templates override. home.html and about.html then extend layout.html using Jinja’s extends mechanism and fill only the unique portion inside the content block. Functionally, the pages still render the same full HTML, but updates now happen in one place.
That single shared layout becomes the control center for site-wide styling. Bootstrap is added by copying its CDN-based CSS and JavaScript into layout.html. A container wrapper and a navigation bar are inserted into the shared structure, and a custom main.css stylesheet is introduced for additional styling. To link CSS correctly, the app uses Flask’s url_for to generate the proper path to static/main.css. After that, the homepage’s post rendering is refined by swapping the basic markup inside the posts loop for a dedicated article.html snippet, which formats each post more cleanly.
By the end, the app demonstrates a full template pipeline: render_template for page generation, Jinja loops and conditionals for dynamic content, and template inheritance for scalable design. With the layout in place, switching the site’s framework (like adding Bootstrap) or adjusting global UI elements requires editing one file, not every route template—setting up the project for the next step: building and validating forms for real user input instead of dummy data.
Cornell Notes
Flask templates replace hardcoded HTML strings with reusable HTML files stored in a templates/ directory. Routes use render_template to render home.html and about.html, and Jinja lets templates loop over passed-in data (e.g., a list of post dictionaries) and print fields like title, author, content, and date posted. Templates also support conditionals to set the browser tab title with a default when no title variable is provided. To eliminate duplicated markup across pages, a parent layout.html defines shared structure and a block content that child templates override using extends. This makes site-wide changes—like adding Bootstrap, navigation, and global CSS—manageable by editing only layout.html.
Why does returning raw HTML from Flask routes become a problem, and how do templates fix it?
How does Flask pass dynamic data into a template, and how does Jinja render it?
What conditional behavior is implemented for page titles, and where does it show up?
What is template inheritance in Jinja, and how does layout.html reduce duplication?
How are Bootstrap and custom styles integrated across all pages?
Review Questions
- How would you modify the template to display only posts with a specific author name?
- What changes would be required if you wanted to add a new shared block besides block content in layout.html?
- Where in the template should you place logic that affects the browser tab title, and why?
Key Points
- 1
Create a templates/ directory and move full HTML documents into home.html and about.html instead of returning raw HTML strings from routes.
- 2
Use render_template in Flask route functions to render templates and pass variables into templates as keyword arguments.
- 3
Use Jinja for loops ({% for ... %}) and variable interpolation ({{ ... }}) to render repeated dynamic content like blog posts.
- 4
Add Jinja if/else conditionals to implement defaults (such as a default page title when no title variable is provided).
- 5
Eliminate duplicated markup by using template inheritance: put shared structure in layout.html and override only block content in child templates.
- 6
Integrate site-wide styling by adding Bootstrap and global navigation/CSS to layout.html so every inherited page updates automatically.
- 7
Link static assets like main.css using url_for('static', filename='main.css') to avoid hardcoding paths.