Overriding Templates
You can replace any built-in template with your own version. Templates must be valid MiniJinja and receive the same context variables described in the MiniJinja Context chapter.
Choosing an Override Method
Use .template() for a single template you want to keep inline or load with include_str!:
AdminApp::new()
// home.html is the dashboard landing page shown after login
.template("home.html", include_str!("../templates/admin/home.html"))
// ...
Use .template_dir() when you're overriding several templates and prefer to keep them as files on disk:
AdminApp::new()
.template_dir("templates/admin")
// ...
Both methods can be combined. .template() always wins over .template_dir() regardless of call order.
Example: Custom Home Page
Create templates/admin/home.html:
{% extends "layout.html" %}
{% block title %}Dashboard — {{ admin_title }}{% endblock %}
{% block content %}
<div class="max-w-2xl">
<h1 class="text-2xl font-bold text-zinc-900 mb-2">Welcome</h1>
<p class="text-zinc-500">You are logged in to {{ admin_title }}.</p>
</div>
{% endblock %}
Register it in your app setup:
AdminApp::new()
.title("My App")
.template_dir("templates/admin")
.auth(...)
// ...
Template Inheritance
The built-in templates use MiniJinja's {% extends %} / {% block %} system. layout.html defines two blocks:
{% block title %}— the<title>tag content{% block content %}— the main page body inside the scrollable content area
When writing a replacement for any page template (e.g. list.html, form.html, home.html), extend layout.html to get the sidebar, header, and flash messages for free:
{% extends "layout.html" %}
{% block title %}{{ entity_label }} — {{ admin_title }}{% endblock %}
{% block content %}
<!-- your page content here -->
{% endblock %}
If you replace layout.html itself, the page templates that extend it will use your version automatically.
Adding New Templates
You can add templates with names that don't correspond to any built-in. These are available for use in custom Axum routes that call the renderer directly, or via {% include %} from other templates:
AdminApp::new()
.template("widgets/stat_card.html", include_str!("../templates/stat_card.html"))
// ...
Partial Overrides via {% include %}
The list page renders its table section by including list_table.html. Overriding list_table.html alone lets you change how rows are displayed without replacing the full list page (search bar, filters, bulk actions, etc.).
Notes
- Template files are loaded once at startup. Editing files on disk after the server starts has no effect until restart.
- Template errors are caught at render time and return an HTML error message in development, so a broken override will not panic the server.
- The
basenamefilter is available in all templates:{{ path | basename }}returns the filename portion of a path string.