Tech Stack Walkthrough

Templating with Templ

Summary

In this video, I cover HTML templating in Go, starting with the standard library approach. I demonstrate how to render basic HTML files, add dynamic data using template variables, and create reusable base templates. Then, I introduce Templ as a more powerful alternative, showing how it offers better developer experience with Go-like syntax, IDE support, and type safety through code generation. I showcase how to rewrite our templates using Templ's .templ files and explain why features like compilation to Go code and compile-time checks make it advantageous for larger applications. The video ends with a preview of adding interactivity using HTMX in future lessons.

Transcript

In the last episode, we left the view part of MSE in a bit of an unfinished place. So in this episode, I want to give you an introduction on how to work with HTML templates to create views, and more specifically, how you can do that with Templ. But first, let me show you how this works in the standard library. A typical HTML file will look something like this. And the way we can actually render this in the browser using the standard library is we could create a render.html file function here, and it's going to return a byte slice and error. Then we need to grab the file, and here we can use the template packet from the standard library to say parse files. We're going to get our imports there, and then we just need to provide it with the path to the template. And then handle the error. Next, we need to have it into a data type that our handlers can deal with and can understand. So we need what I'm going to call an HTML buffer, where we're going to say error HTML file, and then we're going to call execute, which will write the contents of the file to our buffer. HTML file, and then other argument once it's data, which we have none right now. Again, handle the error, and if there's no error, we're just going to say HTML buffer.bytes on nil. And I need to parse the HTML buffer. In our main function, then we can do as we did earlier, using our handle func. So if we just get the template bytes out from our render.html file function, we can then say html, http, handle func. Can we not? We can now get to root func. And now we need to set some headers so that the browser is aware of what type of content we are sending it. So we say header sets, and then the content type, as we did with our JSON API before. But we're going to parse it text, slash HTML. And then we need to write to the response writer. So let's say, w write the template, handle the error, that we're not really handling. And here we can just, let's just panic if there's an error, doesn't really matter for now. Great. So if we go out and run this, we will actually see this in the browser, and we have an error. We do not have an error because I need to say, listen, listen and serve, and we're going to do that on port 8080. Now we are running, good. So let me just grab browser window here, and then see the file being rendered in the browser. Now it would be great if we could also show some dynamic data in this template as well. So let's say we have a blog that allows for guest writers, and we would like to dynamically show the name of the guest writer on each blog post. So we can't just hard code this in the template we're looking at right now, because that could become troublesome, right? We want to be able to dynamically load data into these templates. Luckily, we can do this by simply going in here and saying, let's just imagine that this is some sort of box component or whatever to show like a card for the blog post. Let's say like I am a blog post written by, and then we declare the variable like so. So this is the syntax to tell the template to render whatever is passed here in this place. And we also need to go into main.go. And in here, we are now actually passing some data. As you can see, the second argument here is data. And let's just say that our guest writer is John Carmack. Great. So that's all we need with this in place. We can run our file again and go into our HTML file browser here and say refresh. And you can see it's a blog post written by John Carmack. But what if we want to have more variables? We can simply just pass the struct. So let's create a struct called something like baseViewData that has, let's say, author string and then pub, I can't spell, publicationDate, that's also a string. And now instead of John Carmack here, we're going to pass baseViewData, still going to be John Carmack for now. String time is going to be time, now. And let's get the string versions of this, great. Now we just need to update our template. So now instead of just a dot, we need to specify the name of the variable. So it's going to be author. And let's say written by author and then published at. And then it was publicationDate. Great. So now if we go back and run this, let me just bring in the update. The browser, you can see we now have two variables. You have the author and the publicationDate. Awesome. All good. But you can imagine if we need to provide all of this HTML every time we create a page, this can quickly become unmanageable or unmaintainable at least. So what we can do is we can use, or we can turn parts of it into a template or a base template as we're going to call it in this case. So we do that by saying define and then let's call it base and end. So now this entire thing becomes something we can reference as a template. And then we want to have between the body is where we're going to place our actual contents. And doing this, and to do this, we need to say template content. So end. Now anything past here that is defined as a content will be injected between this start and end. Let's just quickly grab this right here, jump out, and then we're going to create a home.html file. And here we again need to define this as a, we call it content, and also where does it end. There we go. Lastly, we need to update our render file function here. So we're no longer going to be just executing this file. We're going to be saying execute template. So now we need to specify the template. It's going to be, let's just say base here. And we also need to provide it with another file, the home file. There we go. Awesome. Now we have everything in place that we should be able to say go runmin.go. We cannot. Okay. Let's see. It has end, right? I am holding something wrong here. It's still the one template. Yes. Home. We also define the content. Yes. And it's complaining that it has an unexpected end. All right. We can fix this. So we still define base. Yes. And we're going to say end. Yes. And we're going to be saying not end to the template. So there we go. And we still have the same as before. Great. Now we can reuse our base template for all of our views. And this looks quite straightforward, right? Well, it's because it is. We can still do loops, if statements and so forth using the standard library, even though the syntax can look a bit janky. So why would you want to use something else? And why should that be templ? Well, if you take a look at the landing page, we can see that the bullet points mentioning compilation down to perform and go code that if statements and loops are the same syntax. And if you take a look at the code example, we can see that this looks very much like go code. So a big part of what makes templ so good is the developer experience. It gets food syntax. That's very similar to JSX. It has IDE support. It has a very performant code and type safety through code generation and compile time checks. For example, if we had misspelled author earlier or typed it a slower case in a template, it would not have worked and wouldn't have known until runtime. Since these templates are already generated for us, it will lead to much better performance compared to runtime parsing. Composition, reusability, as you'll soon see, also becomes much easier, leading to better maintainability. So staying in one language as much as possible helps us write better code faster by reducing context switching. So let's rewrite what we have now to templ. To rewrite our views using templ, we need to create files with the dot templ extension. When you have installed the templ CLI, you can start running commands like templ generate or templ format, and those commands will target templ dot templ files and then put out go files. Right. So to begin, let's create a views directory. Let's just create a views dot templ. It's going to be package views. Now, templ components or pages, let's just call them components, functions very similar to a go function or look very similar to a go function. You just provide the templ keyword, the name of the component, and then inside the brackets, you put the HTML. So what we have from earlier in our base HTML file, this thing right here, we can simply just grab, put it in, and there we go. Now, in templ, you specify a variable or indicate where a variable goes by only one set of brackets. So we can just say like this, and they have a special variable or slice that gets injected from templ. So we can specify that spread whatever we put inside of this component between the body text here. Right. And now we essentially have what we had before with our base template. So let's just add our home component as well. And we can, again, go in and grab this thing right here. And now, since everything is going to be compiled down to go, we can just specify our expected payload, our view data, whatever we want to call it here. So let's create a home view data of type struct. And we need the author to be a string. And we need the publication date, not data, as I had written earlier. And then we simply just pass this as an argument. So data, home view data. Again, only one set of brackets. And data.author, data.publicationDate. And there we go. Now, if we jump out now and say templ generate, we can see that it generated some files for us. And we now have this go file that has all of the go code required to build the HTML. And we need to also add this to our go.mod file. Now, we can see that we actually have syntax highlighting. We have linters. We have all of that going for us. So we can actually edit our little mistake here. And I misspelled it. So it's publicationDate. And we get some nice errors. Publication. There we go. To add this to our handlers, we simply go back into our main.go file here. And instead of writing to the response writer as we did here, we are just going to say views, home, and then pass it home data. The author is going to be this guy right here, John Carmack. Again, am I spelling that correctly? And time now. And string. There we go. And then we need to call render. And render needs to get past the context. And the write function. And we only have one variable. And there we go. Now we can actually just delete this thing right here. And that's all we need to do. So this feels like just working with go, right? It's amazing. So now say go run main.go. Okay. We, of course, need to generate once more. And now we still have an issue. Okay. Yeah. So, like, every time we make changes to the templ files, we need to regenerate it. So this is what it's complaining about now. Okay. Can I run it? I can run it. Let me bring up the browser again and go to localhost 8080. And we can see we have the same thing as before. Now, we can do a lot more with Templ, and they have a lot more features that we're going to explore later in this course. But for now, you should have a basic understanding of how templating works from a standard library perspective, and how it works with Templ, and why we want to use something like Templ. Because as our application grows, the maintainability and the type safety is going to pay dividends down the road. So stick to it. If things don't really make sense, it will soon. But next up, we're going to start to explore how we can add interactivity to our application using the HTMX library.

Early Access

$95 $65 USD

Get access

Invoices and receipts available for easy company reimbursement