What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use Flask’s render_template() to render a Jinja template file and return the resulting HTML from a view. Import it from flask, place the template in the application’s templates directory, then pass values as keyword arguments:
return render_template('hello.html', person=name)
Flask loads the named file, makes person available to Jinja, renders the file on the server, and returns a string that Flask can send as the response. The examples below follow the Flask 3.1.x documentation.
A minimal working example
Create this project structure:
application.py
templates/
hello.html
In application.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/hello/<name>')
def hello(name):
return render_template('hello.html', person=name)
if __name__ == '__main__':
app.run(debug=True)
Create templates/hello.html:
<!doctype html>
<title>Hello</title>
<h1>Hello {{ person }}!</h1>
Run the application and open /hello/Ada. Flask finds hello.html, substitutes the route value for {{ person }}, and returns the rendered HTML.
What render_template() does
The Flask API defines the function as flask.render_template(template_name_or_list, **context). Its first argument identifies the template; keyword arguments become variables in the Jinja context. The documented return type is str.
#1 Best Overall
| Part | What to provide | What happens |
|---|---|---|
template_name_or_list |
A template filename, a Jinja Template object, or a list of names/objects |
Flask renders the first template that exists when a list is supplied |
**context |
Named values such as user=user or items=items |
Jinja exposes those names while evaluating the template |
| Return value | None required from your view | A rendered string, which a view may return directly |
A normal view therefore needs no explicit response construction:
@app.route('/profile')
def profile():
user = {'name': 'Ada', 'role': 'Engineer'}
return render_template('profile.html', user=user)
If you need to add headers or choose a status code, wrap the rendered string with make_response():
from flask import Flask, make_response, render_template
@app.route('/download-page')
def download_page():
html = render_template('report.html', title='Monthly report')
response = make_response(html, 200)
response.headers['X-Report-Version'] = '1'
return response
Where Flask searches for templates
For a single-file application, Flask conventionally looks for a folder named templates next to the module containing the application. The constructor’s default is template_folder='templates'.
application.py
templates/
hello.html
For a package-based application, put the folder inside the package:
Recommended Free Tools
application/
__init__.py
templates/
hello.html
You can organize files below that directory. Refer to a nested file with a slash:
templates/
admin/
dashboard.html
return render_template('admin/dashboard.html')
If your files live elsewhere, configure the application with an explicit template folder when creating the Flask instance. Keep the configured path and the name passed to render_template() consistent.
Rank #2
Passing values into a template
Scalar values
Pass strings, numbers, booleans, or other Python values as keyword arguments:
@app.route('/welcome')
def welcome():
return render_template('welcome.html', title='Welcome', visits=12, signed_in=True)
Use the names in Jinja:
<title>{{ title }}</title>
<p>Visits: {{ visits }}</p>
{% if signed_in %}
<p>Signed in</p>
{% endif %}
Dictionaries and objects
Pass a dictionary under one context name and access its keys or attributes in the template:
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →@app.route('/profile')
def profile():
user = {'name': 'Ada', 'email': '[email protected]'}
return render_template('profile.html', user=user)
<h1>{{ user.name }}</h1>
<a href="mailto:{{ user.email }}">{{ user.email }}</a>
For a dictionary whose keys are not valid attribute-style names, use bracket notation such as {{ user['display-name'] }}.
Lists and loops
Collections are passed the same way and iterated with Jinja control statements:
@app.route('/tasks')
def tasks():
tasks = [
{'title': 'Write tests', 'done': True},
{'title': 'Deploy app', 'done': False},
]
return render_template('tasks.html', tasks=tasks)
<ul>
{% for task in tasks %}
<li>
{% if task.done %}<del>{{ task.title }}</del>{% else %}{{ task.title }}{% endif %}
</li>
{% else %}
<li>No tasks yet.</li>
{% endfor %}
</ul>
Choosing a fallback template
The first parameter may be a list. Flask renders the first entry that exists, which is useful when a theme or optional override is available:
return render_template(['custom/home.html', 'home.html'], headline='News')
The list can contain template names or template objects. If none of the candidates exists, template loading fails.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Flask’s built-in template context
In a request, Flask makes several helpers available to Jinja in addition to the values you pass. The standard context includes config, request, session, g, url_for(), and get_flashed_messages().
<nav>
<a href="{{ url_for('hello', name='Ada') }}">Greeting</a>
{% if session.get('user_id') %}
<span>Signed in</span>
{% endif %}
</nav>
request, session, and g depend on an active request context. They are not available if you render a template in code that is running outside a request unless you explicitly establish the appropriate Flask context.
HTML escaping and safe data
Flask uses Jinja for rendering. With render_template(), autoescaping is enabled by default for templates ending in .html, .htm, .xml, .xhtml, and .svg. A value such as a submitted comment is therefore emitted as text rather than interpreted as markup.
@app.route('/echo')
def echo():
return render_template('echo.html', message='<script>alert(1)</script>')
<p>{{ message }}</p>
Do not disable autoescaping casually. Flask documents Markup and Jinja’s |safe filter for content you have deliberately verified as safe:
<div class="trusted-copy">{{ trusted_html|safe }}</div>
Never apply |safe to untrusted user input merely to make formatting appear. Review the complete value and its origin before opting out of escaping.
Putting Python data into JavaScript
Pass the value into the template and use Jinja’s tojson filter inside a script block. This produces valid, safely rendered JavaScript data:
@app.route('/chart')
def chart():
points = [{'day': 'Mon', 'value': 4}, {'day': 'Tue', 'value': 7}]
return render_template('chart.html', points=points)
<script>
const points = {{ points|tojson }};
console.log(points);
</script>
Diagnosing template errors
TemplateNotFound
Flask’s tutorial demonstrates TemplateNotFound when a requested file has not been created. Check these items in order:
- Confirm the file exists under the application’s configured template folder.
- Check capitalization and spelling; a request for
hello.htmldoes not matchHello.htmlon case-sensitive filesystems. - Use a slash for nested directories, such as
admin/dashboard.html. - Verify that the
Flaskconstructor is pointing at the folder you actually use. - Restart the development server after moving files if your tooling has not detected the change.
The page shows an empty or unexpected value
- Compare the context name with the Jinja name exactly:
render_template('x.html', person=name)requires{{ person }}. - Check whether a conditional or loop is receiving an empty collection.
- For dictionaries, use bracket notation when a key contains punctuation or conflicts with an attribute.
- If the value comes from
request,session, org, make sure the rendering occurs during a request context.
Markup appears as text
That is usually autoescaping working as designed. Keep the default behavior for untrusted content. Only mark HTML as safe after validating that the complete value is trusted and intended to be interpreted as markup.
JavaScript fails after embedding data
Do not concatenate Python representations into a script manually. Pass the value through the template and use {{ value|tojson }}, then inspect the browser console for errors in the resulting data.
Testing a view that uses render_template()
Use Flask’s test client to request the route and assert on the rendered response. This exercises template lookup and context wiring without requiring a real browser:
import pytest
from application import app
@pytest.fixture
def client():
app.config.update(TESTING=True)
with app.test_client() as client:
yield client
def test_hello(client):
response = client.get('/hello/Ada')
assert response.status_code == 200
assert b'Hello Ada!' in response.data
A test that receives TemplateNotFound is valuable: it catches an incorrect filename or package layout before deployment. Tests can also verify escaped output by requesting a route with characters such as < and checking that they are not returned as executable markup.
Rendering performance and deployment details
Templates execute on the server before the response reaches the browser. Keep expensive database queries and network calls out of the template; load the data in the view, then pass the prepared values as context. Reuse a stable template structure and keep presentation logic in Jinja rather than embedding large Python computations in expressions.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchBest Value
When deploying behind a production WSGI server, the template directory must be included in the application package or deployment artifact. A route can return the rendered string directly, while make_response() is the appropriate point for custom headers, cookies, or status codes.
Or skip the browser setup
If your goal is to capture the HTML produced by a deployed Flask route, ScreenshotNeo is the quickest API option: it removes cookie banners, newsletter popups, and chat widgets before capture, bills only clean shots, and reports the result through X-Page-Verdict and X-Billed headers. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Point the request at a publicly reachable Flask URL (the example below uses the documented Stripe URL; replace it with your deployed route):
See the full parameter reference in the ScreenshotNeo documentation.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));
ScreenshotNeo supports PNG, JPEG, WebP, and PDF output, plus full-page capture, CSS-selector element capture, device and viewport controls, retina scale, custom CSS or JavaScript, waits for selectors or network idle, request blocking, cookies and headers, geolocation, timezone, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture, and a usage API. Every feature is available on every plan. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Frequently Asked Questions
How do I refer to a template inside a subdirectory?
Use the path relative to the configured templates folder, with forward slashes—for example, render_template('admin/dashboard.html').
Can one view choose between two template files?
Yes. Pass a list as the first argument; Flask renders the first name or template object in that list that exists.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Free tools Windows power users keep installed
One-click scans. No signup required.

