HTML Templating (Optional)
1. Welcome and Icebreaker:
Welcome! Today, we’re diving into server-side HTML templating in Rust with Axum.
Icebreaker: Have you ever used a server-rendered website? What did you like or dislike about it? Share your experience!
2. Outline the Session:
Here’s what we’ll cover in this session:
- Concept Explanation: What is HTML templating? Why and how do we use it in Axum?
- Live Coding Demonstration: Render HTML using a template engine (Askama and Tera).
- Tasks: Practice by building your own HTML endpoint and experimenting with templates.
Learning Outcomes:
- Understand the basics of server-side HTML templating in Rust.
- Integrate a template engine (Askama or Tera) with Axum.
- Pass dynamic data from handlers to templates.
- Compare compile-time and runtime template checking.
Expectations:
- Ask questions and try rendering dynamic HTML with your own data!
- Experiment with both Askama and Tera for a broader perspective.
3. Concept Explanation:
HTML templating allows you to generate dynamic HTML on the server, making it possible to serve personalized or data-driven pages. In Rust, popular template engines include:
- Askama: Compile-time checked templates (safer, errors caught at compile time).
- Tera: Runtime checked templates (more flexible, but errors may appear at runtime).
Why use templating in Axum?
- Cleanly separate your Rust logic from HTML presentation.
- Easily render dynamic content (e.g., user profiles, lists, dashboards).
- Reuse templates for consistent layouts.
How does it work?
- Define a template file (e.g.,
hello.html). - Create a Rust struct representing the data to inject.
- Render the template in your handler, passing in the data.
4. Live Coding Demonstration:
Add askama = "0.16" alongside Axum 0.8 and Tokio 1. This manual rendering pattern needs no integration crate; alternatively see the Askama lessons for askama_web.
use askama::Template;
use axum::{response::Html, http::StatusCode, Router, routing::get};
#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> {
name: &'a str,
}
async fn hello() -> Result<Html<String>, StatusCode> {
let template = HelloTemplate { name: "Axum" };
template.render().map(Html).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/hello", get(hello));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
hello.html (in your templates/ folder):
<!DOCTYPE html>
<html>
<head><title>Hello</title></head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
Step-by-step:
- Add
askamaandaxumto yourCargo.toml. - Create a
templates/hello.htmlfile. - Define a struct with
#[derive(Template)]and atemplateattribute. - Render the template in your handler and return
Result<Html<String>, StatusCode>so render failures do not panic.
Best Practices:
- Use compile-time checked templates for safety (Askama).
- Keep templates simple and focused on presentation.
- Organize templates in a dedicated folder (e.g.,
templates/).
Common Pitfalls:
- Forgetting to add template files to your project (they must be present at compile time for Askama).
- Not handling template rendering errors (use proper error handling in production).
- Mixing too much logic into templates—keep logic in Rust, presentation in HTML.
Tera Example: Tera is another popular engine. Its usage is similar, but templates are checked at runtime. Try both to see which fits your workflow!
5. Tasks
- Create a List Template:
- Make a template that displays a list of items (e.g., a shopping list or todo list).
- Pass a vector of items from your handler to the template.
- Dynamic Data:
- Modify your handler to accept a query parameter (e.g.,
?name=YourName) and display it in the template.
- Modify your handler to accept a query parameter (e.g.,
- Try Tera:
- Replace Askama with Tera in your project. Render the same data using Tera templates. Compare the developer experience and error handling.
Bonus:
- Add error handling for template rendering.
- Experiment with template inheritance (layouts) if your engine supports it.
Happy coding!