> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Functions

> Built-in helper functions and custom template functions

The view engine provides built-in template functions that make your templates more expressive. This guide covers all available functions and how to add your own.

## Built-in Functions

These functions are available in all templates.

### Data Structure Functions

#### dict

Creates a map from key-value pairs:

```html theme={null}
{{$user := dict "Name" "Alice" "Age" 30 "Admin" true}}
<p>{{$user.Name}} is {{$user.Age}} years old</p>
```

#### list

Creates a slice from values:

```html theme={null}
{{$fruits := list "apple" "banana" "cherry"}}
<ul>
{{range $fruits}}
    <li>{{.}}</li>
{{end}}
</ul>
```

### String Functions

#### upper

Converts a string to uppercase:

```html theme={null}
<span class="status">{{upper .Data.Status}}</span>
<!-- "active" → "ACTIVE" -->
```

#### lower

Converts a string to lowercase:

```html theme={null}
<div class="type-{{lower .Data.Type}}">
<!-- "Featured" → "featured" -->
```

#### trim

Removes leading and trailing whitespace:

```html theme={null}
<p>{{trim .Data.Description}}</p>
```

#### contains

Checks if a string contains a substring:

```html theme={null}
{{if contains .Data.Email "@company.com"}}
    <span class="internal">Internal User</span>
{{end}}
```

#### replace

Replaces occurrences of a substring:

```html theme={null}
{{replace .Data.Content "\n" "<br>" -1}}
<!-- Replace all newlines with <br> -->
```

The third argument is the maximum number of replacements (-1 means all).

#### split

Splits a string into a slice:

```html theme={null}
{{$parts := split .Data.Tags ","}}
{{range $parts}}
    <span class="tag">{{trim .}}</span>
{{end}}
```

#### join

Joins a slice into a string:

```html theme={null}
<meta name="keywords" content="{{join .Data.Keywords ", "}}">
```

#### hasPrefix

Checks if a string starts with a prefix:

```html theme={null}
{{if hasPrefix .Data.URL "https"}}
    <span class="secure">Secure</span>
{{end}}
```

#### hasSuffix

Checks if a string ends with a suffix:

```html theme={null}
{{if hasSuffix .Data.Filename ".pdf"}}
    <span class="file-icon">PDF</span>
{{end}}
```

### Summary Table

| Function | Description | Example |
| - | - | - |
| `dict` | Create a map | `{{dict "key" "value"}}` |
| `list` | Create a slice | `{{list 1 2 3}}` |
| `upper` | Uppercase string | `{{upper .Data.Name}}` |
| `lower` | Lowercase string | `{{lower .Data.Name}}` |
| `trim` | Trim whitespace | `{{trim .Data.Text}}` |
| `contains` | Check substring | `{{if contains .Data.Text "hello"}}` |
| `replace` | Replace substring | `{{replace .Data.Text "old" "new" -1}}` |
| `split` | Split string | `{{split .Data.Tags ","}}` |
| `join` | Join slice | `{{join .Data.Items ", "}}` |
| `hasPrefix` | Check prefix | `{{if hasPrefix .Data.URL "https"}}` |
| `hasSuffix` | Check suffix | `{{if hasSuffix .Data.File ".go"}}` |

## Go's Standard Functions

Go's `html/template` package provides these built-in functions:

### Comparison Functions

| Function | Description |
| - | - |
| `eq` | Equal |
| `ne` | Not equal |
| `lt` | Less than |
| `le` | Less than or equal |
| `gt` | Greater than |
| `ge` | Greater than or equal |

```html theme={null}
{{if eq .Data.Status "active"}}Active{{end}}
{{if gt .Data.Count 10}}Many items{{end}}
{{if le .Data.Stock 5}}Low stock{{end}}
```

### Logic Functions

| Function | Description |
| - | - |
| `and` | Logical AND |
| `or` | Logical OR |
| `not` | Logical NOT |

```html theme={null}
{{if and .Data.LoggedIn .Data.IsAdmin}}Admin controls{{end}}
{{if or .Data.Error .Data.Warning}}Alert{{end}}
{{if not .Data.Disabled}}Enabled{{end}}
```

### Other Standard Functions

| Function | Description |
| - | - |
| `len` | Length of array, slice, map, or string |
| `index` | Index into array, slice, or map |
| `print` | Format with default formatting |
| `printf` | Format with format string |
| `println` | Format with newline |
| `html` | HTML escape |
| `js` | JavaScript escape |
| `urlquery` | URL query escape |

```html theme={null}
<p>{{len .Data.Items}} items</p>
<p>First: {{index .Data.Items 0}}</p>
<p>{{printf "%.2f" .Data.Price}}</p>
```

## Adding Custom Functions

Add your own functions via the `Funcs` option:

```go theme={null}
import (
    "html/template"
    "strings"
    "time"

    "github.com/go-mizu/mizu/view"
)

engine := view.New(view.Config{
    Dir: "./views",
    Funcs: template.FuncMap{
        // Simple function
        "greet": func(name string) string {
            return "Hello, " + name + "!"
        },

        // Function with multiple arguments
        "pluralize": func(count int, singular, plural string) string {
            if count == 1 {
                return singular
            }
            return plural
        },

        // Date formatting
        "formatDate": func(t time.Time, layout string) string {
            return t.Format(layout)
        },

        // Word count
        "wordCount": func(s string) int {
            return len(strings.Fields(s))
        },

        // Truncate with ellipsis
        "truncate": func(s string, length int) string {
            if len(s) <= length {
                return s
            }
            return s[:length] + "..."
        },
    },
})
```

### Using Custom Functions

```html theme={null}
<h1>{{greet .Data.Name}}</h1>

<p>{{.Data.Count}} {{pluralize .Data.Count "item" "items"}}</p>

<p>Published: {{formatDate .Data.CreatedAt "January 2, 2006"}}</p>

<p>{{wordCount .Data.Content}} words</p>

<p>{{truncate .Data.Description 100}}</p>
```

## Common Custom Functions

### Date Formatting

```go theme={null}
"formatDate": func(t time.Time, layout string) string {
    return t.Format(layout)
},

"timeAgo": func(t time.Time) string {
    duration := time.Since(t)
    switch {
    case duration < time.Minute:
        return "just now"
    case duration < time.Hour:
        return fmt.Sprintf("%d minutes ago", int(duration.Minutes()))
    case duration < 24*time.Hour:
        return fmt.Sprintf("%d hours ago", int(duration.Hours()))
    default:
        return fmt.Sprintf("%d days ago", int(duration.Hours()/24))
    }
},
```

```html theme={null}
<time>{{formatDate .Data.CreatedAt "Jan 2, 2006"}}</time>
<span>{{timeAgo .Data.CreatedAt}}</span>
```

### Number Formatting

```go theme={null}
"formatNumber": func(n int) string {
    // Simple thousands separator
    s := strconv.Itoa(n)
    if len(s) <= 3 {
        return s
    }
    var result []byte
    for i, c := range s {
        if i > 0 && (len(s)-i)%3 == 0 {
            result = append(result, ',')
        }
        result = append(result, byte(c))
    }
    return string(result)
},

"formatCurrency": func(amount float64) string {
    return fmt.Sprintf("$%.2f", amount)
},
```

```html theme={null}
<p>{{formatNumber .Data.Views}} views</p>
<p>Price: {{formatCurrency .Data.Price}}</p>
```

### URL Helpers

```go theme={null}
"urlEncode": url.QueryEscape,

"activeClass": func(currentPath, linkPath string) string {
    if currentPath == linkPath {
        return "active"
    }
    return ""
},
```

```html theme={null}
<a href="/search?q={{urlEncode .Data.Query}}">Search</a>
<a href="/" class="{{activeClass .Data.CurrentPath "/"}}">Home</a>
```

### Safe HTML (Use Carefully!)

```go theme={null}
"safeHTML": func(s string) template.HTML {
    return template.HTML(s)
},
```

```html theme={null}
{{/* Only use with trusted content! */}}
{{safeHTML .Data.TrustedContent}}
```

### Default Values

```go theme={null}
"default": func(defaultVal, val interface{}) interface{} {
    if val == nil || val == "" || val == 0 {
        return defaultVal
    }
    return val
},
```

```html theme={null}
<img src="{{default "/default-avatar.png" .Data.Avatar}}">
```

## Best Practices

### 1. Keep Functions Pure

Functions should not have side effects:

```go theme={null}
// Good: pure function
"double": func(n int) int { return n * 2 },

// Bad: has side effects
"logAndReturn": func(s string) string {
    log.Println(s)  // Side effect!
    return s
},
```

### 2. Handle Edge Cases

```go theme={null}
"truncate": func(s string, length int) string {
    if s == "" || length <= 0 {
        return ""
    }
    if len(s) <= length {
        return s
    }
    return s[:length] + "..."
},
```

### 3. Use Pipelines for Readability

```html theme={null}
<!-- Good: pipeline style -->
<p>{{.Data.Bio | truncate 200 | upper}}</p>

<!-- Also fine: nested calls -->
<p>{{upper (truncate .Data.Bio 200)}}</p>
```

### 4. Document Your Functions

```go theme={null}
Funcs: template.FuncMap{
    // formatPhone formats a phone number as (XXX) XXX-XXXX
    // Example: "1234567890" → "(123) 456-7890"
    "formatPhone": formatPhone,

    // pluralize returns singular if count is 1, plural otherwise
    // Example: pluralize 1 "item" "items" → "item"
    "pluralize": pluralize,
}
```

### 5. Test Custom Functions

```go theme={null}
func TestTruncate(t *testing.T) {
    tests := []struct {
        input    string
        length   int
        expected string
    }{
        {"hello", 10, "hello"},
        {"hello world", 5, "hello..."},
        {"", 5, ""},
    }

    for _, tt := range tests {
        result := truncate(tt.input, tt.length)
        if result != tt.expected {
            t.Errorf("truncate(%q, %d) = %q, want %q",
                tt.input, tt.length, result, tt.expected)
        }
    }
}
```

### 6. Be Careful with safeHTML

Only use `safeHTML` with content you completely trust:

```html theme={null}
<!-- Safe: your own content -->
{{safeHTML .Data.TrustedContent}}

<!-- Dangerous: user content - let Go escape it -->
{{.Data.UserComment}}  <!-- Auto-escaped -->
```

## Complete Example

```go theme={null}
package main

import (
    "fmt"
    "html/template"
    "strings"
    "time"

    "github.com/go-mizu/mizu"
    "github.com/go-mizu/mizu/view"
)

func main() {
    engine := view.New(view.Config{
        Dir:           "./views",
        DefaultLayout: "default",
        Funcs: template.FuncMap{
            // String helpers
            "truncate": truncate,
            "slugify":  slugify,

            // Date helpers
            "formatDate": formatDate,
            "timeAgo":    timeAgo,

            // Number helpers
            "formatNumber":   formatNumber,
            "formatCurrency": formatCurrency,

            // Logic helpers
            "default":   defaultValue,
            "pluralize": pluralize,
        },
    })

    app := mizu.New()
    app.Use(engine.Middleware())

    // ... routes

    app.Listen(":8080")
}

func truncate(s string, length int) string {
    if len(s) <= length {
        return s
    }
    return s[:length] + "..."
}

func slugify(s string) string {
    return strings.ToLower(strings.ReplaceAll(s, " ", "-"))
}

func formatDate(t time.Time, layout string) string {
    return t.Format(layout)
}

func timeAgo(t time.Time) string {
    d := time.Since(t)
    switch {
    case d < time.Minute:
        return "just now"
    case d < time.Hour:
        return fmt.Sprintf("%dm ago", int(d.Minutes()))
    case d < 24*time.Hour:
        return fmt.Sprintf("%dh ago", int(d.Hours()))
    default:
        return fmt.Sprintf("%dd ago", int(d.Hours()/24))
    }
}

func formatNumber(n int) string {
    return fmt.Sprintf("%d", n) // Simplified
}

func formatCurrency(amount float64) string {
    return fmt.Sprintf("$%.2f", amount)
}

func defaultValue(defaultVal, val interface{}) interface{} {
    if val == nil || val == "" {
        return defaultVal
    }
    return val
}

func pluralize(count int, singular, plural string) string {
    if count == 1 {
        return singular
    }
    return plural
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.