Type-safe HTML templating in Zig

(lalinsky.com)

92 points | by lukaslalinsky 3 hours ago

10 comments

  • weitzj 52 minutes ago
    Thank you for the article. I remember using Google Soy templates roughly 10 years ago. I think this is similar to what is used now when you make Helm templates for example or Go Templates. It has the html escaping, JavaScript escaping etc built in.

    Is there maybe a Zig wrapper for Google soy around?

    • lukaslalinsky 44 minutes ago
      Fun fact, I actually also had a fork of the Google Closure Templates that added support for Python. I used it briefly. I completely forgot about it, but I remember now I did enjoy using it back then. https://pythonhosted.org/Flask-Soy/
  • pjmlp 1 hour ago
    > Two years ago, I picked up Zig, and I’d never have imagined I’d be considering web development in such a low-level language.

    This is kind of funny remark, given that the author started in 2000 in Web development, and with PHP 3.0.

    I started in 1997, our distributed systems class had exercises to write CGIs in Perl and C.

    Between 1999 and 2001, most Web development at my employer was in Tcl and C, ASP was naturally a mix of VB and C++ COM components, and there was also the fun to write all those mod_something Apache extensions, and IIS ISAPI modules, naturally in C and C++.

    Ah, Microsoft was also shipping ATLServer, because who wouldn't want to use ATL for Web development, while Borland/Inprise was shipping similar stuff for Delphi and C++ Builder.

    PHP itself, in 3.0, it was still the time that it hardly had much functionality builtin, so it was quite common to write extension modules in C.

    So it is kind of ironic to read coming back to the way things were, before the slow scripting languages takevover in the 2010's, is something no one thought about.

    • lukaslalinsky 1 hour ago
      For me web was always about these scripting languages. Even in the CGI days, I learned to use Perl just enough to do a simple web form. I kind of associated the domain with GC languages, as this kind of work usually involves many temporary strings and dictionaries. In fact, the first compiled language I ever used for a web project was Go, and even that was due to the fact that it has garbage collector.
      • pjmlp 41 minutes ago
        Fair enough, different life experience then.
  • fergie 2 hours ago
    W3C: Why can't we just have <includes src="./header.html"> and be done with it?
  • orf 32 minutes ago
    The linked thread here[1] is… illuminating. The Zig stdlib seems pretty buggy, and the much applauded IO interfaces seem to be struggling with cancellation. The BDFL doesn’t seem to have much to say other than “brush your teeth”

    1. https://ziggit.dev/t/making-it-harder-to-swallow-error-cance...

    • lukaslalinsky 15 minutes ago
      No, that's not the best description. The Io interface is actually one of the best cancellation stories out there. If you combine it with defer/errdefer, you get safer cancellation guarantees than you have in async Rust (you can do async "drop" for example). The only problematic part is the interaction between the Reader/Writer interface and the Io interface, with regards to error handling.
  • Humphrey 2 hours ago
    I love the idea! I could imagine something similar created for other languages too. Perhaps written in a way that a template could be reused in, let's say Django, and a JS React frontend. With all the type-safety that tsx provides.
  • _kidlike 1 hour ago
    sounds very similar to jte.gg for Java
  • jaxxzi 1 hour ago
    I've never quite got the deal with 'template engines'. I have always just done normal programming - in whatever language I'm using - and interspersed outputting raw string literals to create the html.
    • chii 1 hour ago
      and this leads to html injection attacks when there are small errors in your escaping.

      Using a template engine means you systematically ensure that values are escaped (at least, that's one of the reasons i would choose an engine over raw dogging the html).

    • NamlchakKhandro 1 hour ago
      eww gross.
  • vogelke 1 hour ago
    If you can suppress your gag reflex, M4 can do some of what you want. sinclude(...) will include a file and be silent in case of failure. I did something like this for user-login history because I couldn't use PHP or Perl:

        me% cat index.m4
        <!DOCTYPE html>
        <html>
          <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>Better user history</title>
            <link rel="stylesheet" href="local.css">
          </head>
    
          <body>
              <h3>Users</h3>
              <ul>
                <li><em>By username</em></li>
                <ul>
        sinclude(`byuser.m4')dnl
                </ul>
                <li><em>By last login date</em></li>
                <ul>
        sinclude(`bydate.m4')dnl
                </ul>
              </ul>
        sinclude(`footer.m4')dnl
          </body>
        </html>
    
        me% cat byuser.m4
        <li><a href="byuser/A-L">A-L</a></li>
        <li><a href="byuser/M-Z">M-Z</a></li>
    
        me% cat bydate.m4
        <li><a href="bydate/2025">2025</a></li>
        <li><a href="bydate/2026">2026</a></li>
    
        me% cat footer.m4
        <p><em>Last update: syscmd(`date | tr -d "\n"')</em>
    
        me% m4 index.m4 > index.htm
    
        me% cat index.htm
        <!DOCTYPE html>
        <html>
          <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>Better user history</title>
            <link rel="stylesheet" href="local.css">
          </head>
    
          <body>
              <h3>Users</h3>
              <ul>
                <li><em>By username</em></li>
                <ul>
        <li><a href="byuser/A-L">A-L</a></li>
        <li><a href="byuser/M-Z">M-Z</a></li>
                </ul>
                <li><em>By last login date</em></li>
                <ul>
        <li><a href="bydate/2025">2025</a></li>
        <li><a href="bydate/2026">2026</a></li>
                </ul>
              </ul>
        <p><em>Last update: Mon Oct 12 03:08:32 EDT 2026</em>
          </body>
        </html>
  • slopinthebag 2 hours ago
    one thing i love about rust's macros is that you can accomplish this without needing a separate file type or inventing syntax

       #[component]
       pub fn HomePage(user: Option<User>) -> Element {
          rsx! {
             Layout { 
                title: "Home",
                if let Some(u) = user {
                   h1 { "Welcome back, {u.name}!" }
                   a { href: "/users/{u.id}", "Your profile" }
                } else {
                   h1 { "Welcome, guest!" }
                   a { href: "/login", "Log in" }
                }
             }
          }
       }
    • dnautics 2 hours ago
      > you can accomplish this

      That's not HTML though, you have to learn a whole new dsl that must conform to rust parser rules. So, not the same thing at all.

      • josephg 2 hours ago
        You can do actual html. This is Leptos, a Solidjs like rust library:

            #[component]
            fn App() -> impl IntoView {
                let (count, set_count) = signal(0);
                view! {
                    <button
                        on:click=move |_| set_count.set(3) >
                        "Click me”</button>
                    <p>{move || count.get() * 2}</p>
                }
            }
      • slopinthebag 1 hour ago
        i prefer it, but you can also get html if you want

        eg

           html! {
               <div onclick=|_ev: web_sys::MouseEvent| {}>
                  You can type text right into the elements
                  { component }
                  { text_var }
               </div>
            };
    • asimovDev 2 hours ago
      This is just Twig templates, crazy. It's cool that Rust can do this out of a box
      • slopinthebag 2 hours ago
        yep and you get rust's lsp support too, autoformatting, etc.

        this is rsx, but there are other macros that look just like html. i prefer this though, it's much more compact and readable imo

        • lukaslalinsky 1 hour ago
          I'm not a fan of Rust, the overly flexible macros are actually one reason for it, but if you can indeed get LSP support in a custom syntax like this, that's really awesome. Thank you for the example.
    • skavi 1 hour ago
      but you have invented syntax
    • sroerick 2 hours ago
      That is super cool actually
    • eggsandbeer 2 hours ago
      [dead]
  • thetexaslawdog 2 hours ago
    [flagged]