Adding a Page

The internal/ui folder

Frontend pages are located in the /internal/ui/pages folder.

root |--internal | |--ui | |--components // shared components | |--pages // frontend pages | |--index.templ // the app's main template page | |--handlers.go // root handlers | |--style.css // custom style sheet (if you installed tailwind)

The pages folder structure should mirror the structure of the website. Keep handler and page components close to where they are used.

Indirection in code

We recommend keeping indirection in pages to a minimum. This means that sometimes it is better to repeat code in pages than to create a new component that hides the code on a page. There are many tools that will help you manage/refactor repeated code such as find-and-replace, multi-cursors, and so on. The goal is to make the code as easy to read and maintain as possible. Sometimes this principle of simplicity goes against the principle of Do-Not-Repeat-Yourself (DRY), but on balance you gain more than you loose.

For example, this code is much easier to read and maintain than the same code with indirection shown below it.

templ Page() { <h2 class="mt-2 text-2xl font-extrabold">New section</h2> }
templ Page() { @SomeHeadingComponent(SomePropsComponent{ Text: "New section", Size: "2xl", Font: "extrabold", }) }

Example

Lets continue the simple todo list app (mytodo) we started in backend here. The design of mytodo frontend is a single page showing all the todo lists and their items.

Display todo lists

Lets start by modifying the /internal/ui/pages/index.templ to take the todo lists as a parameter and display them.

mytodo |--internal | |--ui | |--pages | |--index.templ
package ui import "github.com/myorg/mytodo/internal/app/list" // add lists as a parameter to the Index templ templ Index(lists []list.List) { <!DOCTYPE html> <html lang="en"> ... header and script tags <body hx-ext="response-targets" hx-target-error="this"> <main class="flex w-full flex-col"> // delete the example code in the main tag and add the following for _, l := range lists { <div class="flex flex-col gap-y-2"> <h2 class="text-2xl font-extrabold">{ l.Name }</h2> <div class="flex flex-col gap-y-2"> for _, item := range list.Items { <div class="flex items-center gap-x-2"> <span>{ item }</span> </div> } </div> </div> } </main> </body> </html> }

Add buttons

Lets include buttons to add todo lists and items. Note that these use hx-post to end points that we will create in the next section and return the HTML to be swapped in.

package ui import "github.com/myorg/mytodo/internal/app/lists" templ Index(lists []lists.List) { <!DOCTYPE html> <html lang="en"> ... header and script tags <body hx-ext="response-targets" hx-target-error="this"> <main class="flex w-full flex-col"> for _, l := range lists { <div class="flex flex-col gap-y-2"> <h2 class="text-2xl font-extrabold">{ l.Name }</h2> <div class="flex flex-col gap-y-2"> for _, item := range list.Items { <div class="flex items-center gap-x-2"> <span>{ item }</span> </div> } <input id="todo-list-item" type="text"/> <button type="button" hx-post={"/list/" + l.ID} hx-include="todo-list-item"> Add Item </button> </div> </div> } <input id="todo-list-name" type="text"/> <button type="button" hx-post="/list" hx-include="todo-list-name"> Add List </button> </main> </body> </html> }

Copyright © 2025