Recommended Free Tools
Use render_template() inside a Flask view, put the requested file in a templates directory, and pass page data as keyword arguments. Flask asks Jinja to render that file and returns the resulting HTML as a string that the view can return directly.
Render your first Jinja template
Import render_template from Flask, create a route, and return the function call. This complete example follows the Flask 3.1.x API:
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 this file relative to application.py:
templates/hello.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
<h1>Hello {{ person }}!</h1>
</body>
</html>
Start the application and visit http://127.0.0.1:5000/hello/Ada. Jinja replaces {{ person }} with the route value, so the page displays “Hello Ada!”. Flask’s quickstart documents this server-side rendering flow in its quickstart.
What render_template accepts and returns
The documented signature is flask.render_template(template_name_or_list, **context). The first argument identifies what to render; keyword arguments become variables in the Jinja context. The documented return type is str, and a view may return that string directly. See the Flask API reference for the complete signature.
#1 Best Overall
| Argument | What to pass | Result |
|---|---|---|
template_name_or_list |
A template name, a Jinja Template object, or a list of names/objects |
Flask renders the named template, or the first entry in a list that exists |
**context |
Keyword arguments such as person=name or user=user |
Those names are available to expressions in the template |
| Return value | No special response wrapper required for ordinary pages | A rendered Unicode string |
For example, a dictionary is passed as one named value, not expanded accidentally:
from flask import render_template
@app.route('/profile')
def profile():
user = {'name': 'Ada', 'role': 'Engineer'}
return render_template('profile.html', user=user)
<h1>{{ user.name }}</h1>
<p>Role: {{ user.role }}</p>
Place templates in Flask’s search folder
By default, Flask looks for a folder named templates next to a single-file application module or inside an application package. The application constructor’s default is template_folder='templates', using a filesystem loader when that folder is configured. These layouts match the official quickstart.
Single-file application
application.py
templates/
hello.html
profile.html
Run the application from the directory containing application.py, and call render_template('hello.html'). The name is relative to the template folder; do not include the folder name itself.
Package application
application/
__init__.py
templates/
hello.html
When you create the Flask object inside application/__init__.py, Flask searches the package’s templates directory. If you intentionally use another directory, configure it explicitly when constructing Flask, for example Flask(__name__, template_folder='web_templates').
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Pass values, lists and conditional data
Every keyword argument after the template name becomes a template variable. Jinja expressions can read attributes and dictionary keys, iterate over lists, and choose content conditionally:
@app.route('/tasks')
def tasks():
tasks = [
{'title': 'Write tests', 'done': True},
{'title': 'Deploy app', 'done': False},
]
return render_template('tasks.html', tasks=tasks)
<h1>Tasks</h1>
<ul>
{% for task in tasks %}
<li>
{{ task.title }}
{% if task.done %}(done){% else %}(open){% endif %}
</li>
{% endfor %}
</ul>
Flask also supplies standard context helpers while a request is active. The documented names include config, request, session, g, url_for(), and get_flashed_messages(). Request-bound values such as request, session, and g are not available when you render outside an active request context. Flask explains this context in its templating guide.
Understand autoescaping before displaying user input
Flask uses Jinja as its template engine. For files ending in .html, .htm, .xml, .xhtml, or .svg, Flask enables autoescaping when the file is rendered with render_template(). Characters such as < and > in an ordinary string are escaped instead of interpreted as markup.
@app.route('/search')
def search():
return render_template('search.html', query='<script>alert(1)</script>')
<p>You searched for: {{ query }}</p>
Do not disable autoescaping casually and do not mark untrusted input as safe. Flask documents Markup and Jinja’s |safe filter as explicit ways to mark content safe; use them only after you have controlled and validated the content. The escaping rules and exceptions are described in the templating documentation.
Rank #3
Insert server data into JavaScript safely
Templates execute on the server before the response reaches the browser. To put a Python value into a script, pass it through the template context and use Jinja’s tojson filter. The Flask quickstart recommends this approach for valid, safely rendered JavaScript data:
@app.route('/dashboard')
def dashboard():
chart = {'labels': ['Mon', 'Tue'], 'values': [3, 5]}
return render_template('dashboard.html', chart=chart)
<script>
const chart = {{ chart|tojson }};
console.log(chart.values);
</script>
Do not build JavaScript by concatenating an unescaped Python string into a <script> block. tojson produces a JavaScript-compatible representation while applying the escaping required by the template context.
Set a status code or headers with make_response
For a normal page, returning the rendered string is enough. If you need to set headers or a status code, wrap the result with make_response:
from flask import make_response, render_template
@app.route('/created')
def created():
html = render_template('created.html')
response = make_response(html, 201)
response.headers['X-Page-Type'] = 'created'
return response
The API documentation shows this pattern for turning rendered output into a response object while retaining control over HTTP metadata.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsUse fallback templates when more than one name is acceptable
The first argument can be a list of template names or template objects. Flask renders the first entry that exists:
@app.route('/notice')
def notice():
return render_template(['notice.html', 'default-notice.html'], message='Maintenance')
This is useful when a deployment can provide a customized file but should fall back to a default. Keep the fallback order deliberate: Flask stops at the first existing entry.
Fix the errors developers see most often
| Symptom | Likely cause | Fix |
|---|---|---|
jinja2.exceptions.TemplateNotFound |
The file is missing, in the wrong directory, or the name differs from the argument | Confirm the file exists under the application’s configured templates folder and that spelling and capitalization exactly match render_template(). Flask demonstrates this failure in its templates tutorial. |
UndefinedError for a variable |
The view did not pass the keyword, or the template uses a different name | Compare the context names, such as user=user with {{ user.name }}, and check conditional branches for missing values. |
BuildError from url_for() |
The endpoint name or required route argument is wrong | Use the view function’s endpoint name and supply every dynamic route value required by that route. |
| HTML appears as text | The value was escaped, or the file extension is not one of Flask’s autoescaped extensions | Keep untrusted content escaped. If trusted markup must be rendered, review the value and an explicit safe-marking operation carefully rather than disabling escaping globally. |
request, session, or g is unavailable |
Rendering is happening outside an active request context | Move the call into a request-handling view or establish the appropriate Flask context for a controlled background operation; do not assume request-bound objects exist everywhere. |
| Changes to a template do not appear | A different file is being loaded, or a cached/browser response is being viewed | Verify the resolved filename and URL, restart the development server if necessary, and hard-refresh the browser. Avoid treating development debugging behavior as a production cache strategy. |
Organize a maintainable render flow
- Keep view functions focused: load or calculate data in Python, then pass named values to the template.
- Use descriptive context names:
render_template('order.html', order=order, errors=errors)is easier to maintain than positional conventions. - Keep presentation in Jinja: use loops and conditionals for display decisions, but keep database access and business rules in Python.
- Choose extensions deliberately: HTML-like extensions receive Flask’s documented autoescaping defaults.
- Test the response: Flask’s test client can request the route and let you assert the status code and expected text without opening a browser.
For a production application, also review the data you pass into templates. Autoescaping protects the HTML output path, but it does not make every use of a value safe in JavaScript, URLs, CSS, or an HTML attribute with special requirements. Use context-appropriate escaping and tojson for script data.
Or skip the browser setup
If your goal is to capture the rendered Flask page as an image or PDF for a test artifact, preview, or documentation, ScreenshotNeo can request a public URL directly. Before capture it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Replace the example URL with a publicly reachable route from your Flask deployment. The parameter names used by other screenshot APIs also work, which can simplify a migration. Full API documentation is at https://screenshotneo.com/docs/.
Best Value
cURL
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://your-domain.example/hello/Ada
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://your-domain.example/hello/Ada",
},
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://your-domain.example/hello/Ada'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', bytes);
ScreenshotNeo includes full-page capture with lazy images loaded, CSS-selector element capture, device presets or custom viewports, dark mode, retina scale, PDF settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Plans include 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Does render_template render in the browser?
No. Jinja runs on the Flask server, and the browser receives the resulting string as the HTTP response.
What should I check first when a page is blank?
Confirm that the route reaches the intended view, the template name points to the configured templates folder, and the template actually emits HTML outside any false conditional branch.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Frequently Asked Questions
Can I return the rendered string without creating a Response object?
Yes. Flask converts a view’s returned string into a response automatically. Use make_response only when you need to set headers or a specific status code.
Why is a request value unavailable in a background task?
request, session, and g are request-bound context objects. A background task does not automatically have the active request context, so pass the data it needs explicitly or arrange an appropriate Flask context.
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.

