My MiniJinja Starter Template

January 2024

Ready To Copy/Paste

This is the basic MiniJinja1 code I use to get started with a new project:

Check out the rust_gimiore for a refined version of this that will eventually take this place.

Old Notes

This is my basic starer as of June, 2025.

---
[dependencies]
anyhow = "1.0.98"
minijinja = { version = "2.9.0", features = ["custom_syntax", "loader"] }
serde = {version = "1.0.219", features = ["derive"] }
---

use anyhow::Result;
use minijinja::{Environment, Value, context};
use minijinja::syntax::SyntaxConfig;
use serde::{Deserialize, Serialize};


#[derive(Deserialize, Serialize)]
struct Payload {
    name: String
}

fn main() -> Result<()> {
    let payload = Payload{ name: "MiniJinja".to_string() };
    output_content(&payload)?;
    println!("Done");
    Ok(())
}

fn output_content(payload: &Payload) -> Result<()> {
    let mut env = get_env();
    env.add_template_owned(
        "template", template_content()
    )?;
    let jinja = env.get_template("template")?;
    let data = Value::from_serialize(payload);
    let output = jinja.render(context!(data => data))?;
    println!("{}", output);
    Ok(())
}


fn get_env() -> Environment<'static> {
    let mut env = Environment::new();
    env.set_syntax(
        SyntaxConfig::builder()
            .block_delimiters("[!", "!]")
            .variable_delimiters("[@", "@]")
            .comment_delimiters("[#", "#]")
            .build()
            .unwrap(),
    );
    env
}

fn template_content() -> String {
r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta<meta charset="UTF-8">
</head>
<body>
    <h1>
        Hello, [@ data.name @]!
    </h1>
</body>
</html>"#.to_string()
}
Output:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta<meta charset="UTF-8">
</head>
<body>
    <h1>
        Hello, MiniJinja!
    </h1>
</body>
</html>
Done

Inline Error Handling

---
[dependencies]
minijinja = { version = "2.9.0", features = ["custom_syntax", "loader"] }
---

use minijinja::{Environment, Value, context};
use minijinja::syntax::SyntaxConfig;

fn main() {
    let name = Value::from("MiniJinja");
    let mut env = get_env();
    env.add_template_owned(
        "example-template", make_template()
    ).unwrap();
    match env.get_template("example-template") {
        Ok(jinja) => {
            match jinja.render(
                context!(name)
            ) {
                Ok(output) => {
                    println!("{}", output);
                }
                Err(e) => { 
                    println!("{}", e);
                }
            }
        }
        Err(e) => {
            println!("{}", e);
        }
    }
}

fn get_env() -> Environment<'static> {
    let mut env = Environment::new();
    env.set_syntax(
        SyntaxConfig::builder()
            .block_delimiters("[!", "!]")
            .variable_delimiters("[@", "@]")
            .comment_delimiters("[#", "#]")
            .build()
            .unwrap(),
    );
    env
}

fn make_template() -> String {
r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta<meta charset="UTF-8">
</head>
<body>
    <h1>
        Hello, [@ name @]!
    </h1>
</body>
</html>"#.to_string()
}
Output:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta<meta charset="UTF-8">
</head>
<body>
    <h1>
        Hello, MiniJinja!
    </h1>
</body>
</html>

Custom Tokens vs. Default Tokens

I use custom tokens for my MiniJinja projects. They are changed using the .set_syntax() call in the get_env() function. Specifically:

  • {% %} becomes [! !]
  • {{ }} becomes [@ @]
  • {# #} becomes [# #]

If find the square brackets easier to track visually. I also find hitting Shift+1 easier than hitting Shift+5.

You can switch to the default tokes by replacing:

let mut env = get_env();

in the main() function with:

let mut env = Environment::new();

and then deleting the get_env() function.

My Go-To Engine

I use MiniJinja for Neopoligen2. My site is currently 2,963 pages. It outputs in less than 10 seconds with an empty cache3. I could probably add some optimizations, but it's plenty fast for me as is. I've yet to run into something that I can't get it to do.

Definitely worth checking out if you're in the market.

-a

end of line

Footnotes

My personal website builder.

And about 1 second with a warm cache.