> ## 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.

# Engine

> Configure the view engine for development and production

The view engine is the core of Mizu's template system. This guide covers all configuration options and how to use them effectively.

## Creating an Engine

Create a view engine with `view.New()`:

```go theme={null}
import "github.com/go-mizu/mizu/view"

engine := view.New(view.Config{
    Dir:           "./views",
    Extension:     ".html",
    DefaultLayout: "default",
    Development:   true,
})
```

## Configuration Options

### Dir

The directory where your templates are stored.

```go theme={null}
view.Config{
    Dir: "./views",  // Default: "views"
}
```

The engine expects this structure inside the directory:

```
views/
├── layouts/       # Layout templates
│   └── default.html
└── pages/         # Page templates
    ├── home.html
    └── about.html
```

### FS

An `fs.FS` filesystem to load templates from. Use this with `embed.FS` in production to bundle templates into your binary.

```go theme={null}
import "embed"

//go:embed views
var viewsFS embed.FS

engine := view.New(view.Config{
    FS: viewsFS,  // Use embedded filesystem
})
```

When `FS` is set, templates are loaded from the embedded filesystem instead of the local filesystem. The directory structure inside the embedded FS should match what you'd have on disk.

### Extension

The file extension for template files.

```go theme={null}
view.Config{
    Extension: ".html",  // Default: ".html"
}
```

You can use other extensions:

```go theme={null}
view.Config{
    Extension: ".tmpl",  // Now looks for .tmpl files
}
```

### DefaultLayout

The layout template to use when rendering pages. This refers to a file in the `layouts/` directory.

```go theme={null}
view.Config{
    DefaultLayout: "default",  // Uses layouts/default.html
}
```

You can override this per-render with `view.Layout()` or disable it with `view.NoLayout()`.

### Delims

Custom template delimiters. Useful if your templates contain `{{` and `}}` literally (common with Vue.js or Angular templates).

```go theme={null}
view.Config{
    Delims: [2]string{"<%", "%>"},  // Default: ["{{", "}}"]
}
```

Now templates use `<% %>` instead:

```html theme={null}
<h1>Hello, <%.Data.Name%></h1>
```

### Development

Enables development mode features:

* **Template reload** - Templates are re-read from disk on every request
* **No caching** - Templates are never cached

```go theme={null}
view.Config{
    Development: true,  // Default: false
}
```

**Important:** Never enable Development mode in production. It significantly impacts performance.

A common pattern is to use an environment variable:

```go theme={null}
view.Config{
    Development: os.Getenv("ENV") != "production",
}
```

### Funcs

Custom template functions to add to all templates.

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

view.Config{
    Funcs: template.FuncMap{
        "uppercase": strings.ToUpper,
        "formatDate": func(t time.Time) string {
            return t.Format("Jan 2, 2006")
        },
    },
}
```

Now you can use these in templates:

```html theme={null}
<h1>{{uppercase .Data.Name}}</h1>
<p>Posted on {{formatDate .Data.CreatedAt}}</p>
```

## Built-in Functions

The view engine provides these functions by default:

| 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"}}` |
| `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"}}`  |

## Engine Methods

### New

Creates a new view engine with the given configuration.

```go theme={null}
engine := view.New(view.Config{
    Dir:         "./views",
    Development: true,
})
```

### Load

Loads and validates all templates at startup. Call this in production to fail fast if templates have errors.

```go theme={null}
engine := view.New(config)

if err := engine.Load(); err != nil {
    log.Fatal("template error:", err)
}
```

This loads all files in `layouts/` and `pages/` directories.

### Clear

Clears the template cache. Useful if you need to reload templates without restarting.

```go theme={null}
engine.Clear()
```

### Render

Renders a page template. The first argument is an `io.Writer` (like `http.ResponseWriter`), followed by the template name, data, and optional render options.

```go theme={null}
err := engine.Render(w, "home", view.Data{
    "Title": "Home",
    "User":  currentUser,
})
```

The template name refers to a file in `pages/` without the extension.

### Middleware

Returns a Mizu middleware that adds the engine to every request context.

```go theme={null}
app := mizu.New()
app.Use(engine.Middleware())
```

After adding this middleware, you can use `view.From(c)` and `view.Render(c, ...)` in handlers.

## Package Functions

### From

Retrieves the engine from a request context. Use this in handlers after adding the middleware.

```go theme={null}
func handler(c *mizu.Ctx) error {
    engine := view.From(c)
    if engine == nil {
        return errors.New("view engine not configured")
    }
    return engine.Render(c.Writer(), "page", data)
}
```

### Render (package-level)

A convenience function that gets the engine from context and renders. This is the most common way to render templates.

```go theme={null}
func handler(c *mizu.Ctx) error {
    return view.Render(c, "home", view.Data{
        "Title": "Home",
    })
}
```

This is equivalent to:

```go theme={null}
engine := view.From(c)
c.Writer().Header().Set("Content-Type", "text/html; charset=utf-8")
c.Writer().WriteHeader(200)
return engine.Render(c.Writer(), "home", data)
```

## Render Options

When calling `Render()`, you can pass options to customize behavior.

### Layout

Override the default layout for this render:

```go theme={null}
view.Render(c, "dashboard", data, view.Layout("admin"))
```

This uses `layouts/admin.html` instead of the default layout.

### NoLayout

Render without any layout (useful for partial responses):

```go theme={null}
view.Render(c, "user-row", data, view.NoLayout())
```

## Template Data Structure

When you render a template, your data is wrapped in a structure:

```go theme={null}
type pageData struct {
    Page    pageMeta      // Template metadata
    Data    any           // Your data
    Content template.HTML // Rendered page (only in layouts)
}

type pageMeta struct {
    Name   string  // Template name (e.g., "home")
    Layout string  // Layout name (e.g., "default")
}
```

**In page templates:**

```html theme={null}
<h1>{{.Data.Title}}</h1>
<p>Template: {{.Page.Name}}</p>
```

**In layout templates:**

```html theme={null}
<title>{{.Data.Title}}</title>
<body>
    {{.Content}}  <!-- Rendered page content -->
</body>
```

## Complete Example

Here's a typical configuration for a production app:

```go theme={null}
package main

import (
    "embed"
    "html/template"
    "os"
    "strings"
    "time"

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

//go:embed views
var viewsFS embed.FS

func main() {
    isDev := os.Getenv("ENV") != "production"

    config := view.Config{
        Extension:     ".html",
        DefaultLayout: "default",
        Development:   isDev,
        Funcs: template.FuncMap{
            "formatDate": formatDate,
            "truncate":   truncate,
        },
    }

    if isDev {
        // Development: load from filesystem
        config.Dir = "./views"
    } else {
        // Production: load from embedded FS
        config.FS = viewsFS
    }

    engine := view.New(config)

    // Preload templates in production to fail fast
    if !isDev {
        if err := engine.Load(); err != nil {
            panic(err)
        }
    }

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

    app.Get("/", homeHandler)
    app.Get("/about", aboutHandler)

    app.Listen(":8080")
}

func formatDate(t time.Time) string {
    return t.Format("January 2, 2006")
}

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

func homeHandler(c *mizu.Ctx) error {
    return view.Render(c, "home", view.Data{
        "Title": "Welcome",
    })
}

func aboutHandler(c *mizu.Ctx) error {
    return view.Render(c, "about", view.Data{
        "Title": "About Us",
    })
}
```

## Error Handling

### Template Errors

The engine returns an `Error` type that wraps template parsing and execution errors:

```go theme={null}
err := engine.Render(w, "page", data)
if err != nil {
    var viewErr *view.Error
    if errors.As(err, &viewErr) {
        log.Printf("Template error (%s %q): %v",
            viewErr.Kind, viewErr.Name, viewErr.Err)
    }
}
```

The `Error` type has:

* `Kind` - "page", "layout", or "template"
* `Name` - Template name
* `Err` - Underlying error (nil if not found)

### ErrNotFound

Returned when a template file doesn't exist:

```go theme={null}
if errors.Is(err, view.ErrNotFound) {
    // Template file not found
}
```

## Best Practices

### 1. Use Development Mode Locally

Always enable development mode during development:

```go theme={null}
Development: os.Getenv("ENV") != "production"
```

### 2. Load Templates in Production

Call `Load()` at startup to catch template errors early:

```go theme={null}
if err := engine.Load(); err != nil {
    log.Fatal(err)
}
```

### 3. Embed Templates for Production

Use `embed.FS` so templates are bundled in your binary:

```go theme={null}
//go:embed views
var viewsFS embed.FS
```

This eliminates the need to deploy template files alongside your binary.

### 4. Keep Custom Functions Pure

Custom functions should be pure (no side effects):

```go theme={null}
// Good: pure function
"uppercase": strings.ToUpper,

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

### 5. Use Consistent Data Structures

Create struct types for complex template data:

```go theme={null}
type PageData struct {
    Title string
    User  *User
    Posts []Post
}

view.Render(c, "home", PageData{
    Title: "Home",
    User:  currentUser,
    Posts: posts,
})
```

Note: When using a struct instead of `view.Data` (which is `map[string]any`), access it directly in templates:

```html theme={null}
<h1>{{.Data.Title}}</h1>  <!-- With view.Data map -->
<h1>{{.Data.Title}}</h1>  <!-- With struct - same access pattern -->
```
