Version: v0.5.1 - Beta.  We welcome contributors & feedback.  THanks!

To Do List (Example)


This example will cover a form-based app that handles both GET and POST http methods.


We assume you have created the starter app.

Within that app, create a new file pages/todo.tht.

Copy and paste each code snippet below into the file.

Main Function

The main function is called by default, when the page is requested via http GET (i.e. when a form is not submitted.)

function main() {
    let tasks = getTasks();
        body: bodyHtml(tasks),
        css: Css.plugin('base'),

Data Functions

We will read and write the list of tasks to the user’s Session. The Session will be cleared when the user closes the browser tab.

(In a real app, you would read and write data to a database using the Db module.)

function getTasks() {
    // Default to an empty list if there are no tasks
    return Session.get('tasks', []);

function setTasks(tasks) {
    Session.set('tasks', tasks);

function addTask(task) {
    let tasks = getTasks();

function deleteTask(taskNum) {
    let tasks = getTasks();

Mode Functions

If the request has a POST parameter named mode, THT will automatically call the corresponding function instead of main.

The method reads the POST param and validates it per the rule defined in the 2nd argument.

// If POST mode='delete'
function modeDelete() {
    // 'taskNum' is an (i)nteger
    let taskNum ='taskNum', 'i');

    return true;

// If POST mode='add'
function modeAdd() {
    // 'task' is a (s)tring
    let task ='task', 's');
    if (task) {
    return true;


The Web.formLink method creates a button that submits data (via a form with hidden fields), without the need for AJAX.

Styles are separated into a css template function. It will be minified and included inline.

function deleteButton(num) {
    let data = {
        mode: 'delete',
        taskNum: num
    return Web.formLink('X', url'/todo', data, 'button-small');

template bodyHtml(tasks) {

        {{ taskFormHtml() }}
        <hr />
        {{ taskListHtml(tasks) }}

    {{ pageCss() }}

template taskFormHtml() {

    <h2>New Task</>

    <form method="post" action="/todo">
        <input type="hidden" name="mode" value="add" />
        <input type="text" name="task" />
        <button type="submit">{{ Web.icon('plus') }} Add Task</>

template taskListHtml(tasks) {


    -- if (!tasks.length()) {
        <i>No tasks.</>
    -- } else {
        -- for (num:task in tasks) {
            <div class="task">
              {{ task }}
              {{ deleteButton(num) }}
        -- }
    -- }

template pageCss() {

    .task {
        border-bottom: solid 1px #eee;
        padding: 1.2rem 1rem;
        position: relative;
    .task form {
        position: absolute;
        right: 0;
        top: 0.8rem;

See Also