Views

Views are etlua (embedded Lua) templates that render your application's HTML with dynamic data.

code
etlua Templates

Embedded Lua in HTML files

layers
Layouts

Wrap pages with shared structure

widgets
Partials

Reusable template fragments

code etlua Syntax

Views use the etlua templating engine which embeds Lua code directly in HTML files.

terminal

Template Tags

Three ways to embed Lua in templates

<%= %> Output (escaped)
<h1><%= title %></h1>
<p>Hello, <%= user.name %>!</p>
<span>© <%= os.date("%Y") %></span>

HTML entities are escaped for security

<%- %> Raw Output (unescaped)
<!-- Include partials -->
<%- Partial("header") %>

<!-- Render HTML content -->
<%- html_content %>

Use for trusted HTML content and partials

<% %> Code Block
<% if user then %>
  <p>Welcome back, <%= user.name %>!</p>
<% else %>
  <p>Please log in.</p>
<% end %>

<% for i, item in ipairs(items) do %>
  <li><%= item.name %></li>
<% end %>

Execute Lua code without output

folder_open Directory Structure

Views are organized in the app/views/ directory with a clear structure.

app/views/
├── layouts/           # Page layouts
│   ├── app/
│   │   └── index.html.etlua
│   └── docs/
│       └── index.html.etlua
├── partials/          # Reusable fragments
│   ├── header.html.etlua
│   └── footer.html.etlua
├── welcome/           # Controller views
│   ├── index.etlua
│   └── index+iphone.etlua
├── docs/
│   └── index.etlua
└── 404.etlua          # Error pages

layers Layouts

Layouts wrap your page content with shared HTML structure like headers, footers, and assets.

description

Layout Example

app/views/layouts/app/index.html.etlua

<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title><%= title or "My App" %></title>
  <link rel="stylesheet" href="<%= PublicPath("/app.css") %>" />
</head>
<body>
  <%- Partial("header") %>

  <main>
    @yield  <!-- Page content inserted here -->
  </main>

  <%- Partial("footer") %>
  <%- RefreshPageForDevMode() %>
</body>
</html>

info Key elements:

check_circle @yield — marks where page content is inserted
check_circle PublicPath() — generates paths to public assets
check_circle RefreshPageForDevMode() — enables live reload

widgets Partials

Partials are reusable template fragments that can be included anywhere.

header.html.etlua
<header class="py-6">
  <nav>
    <a href="/">My App</a>
    <a href="/about">About</a>
  </nav>
</header>
Usage
<!-- Include a partial -->
<%- Partial("header") %>

<!-- From components folder -->
<%- Partial("components/card") %>
send

Passing Parameters to Partials

Send data to partials using the second argument

Calling with parameters

<!-- Pass a table of parameters -->
<%- Partial("user_card", {
  name = "John Doe",
  email = "john@example.com",
  role = "Admin"
}) %>

<!-- Pass existing variables -->
<%- Partial("product_item", {
  product = product,
  show_price = true
}) %>

Accessing in partial

<!-- user_card.html.etlua -->
<div class="card">
  <h3><%= name %></h3>
  <p><%= email %></p>
  <span class="badge"><%= role %></span>
</div>

<!-- With defaults -->
<% role = role or "User" %>

info How it works:

check_circle Parameters are passed as the second argument to Partial()
check_circle All keys in the table become local variables in the partial
check_circle Use or operator to set default values for optional params

view_quilt Content Blocks

Use ContentFor to define content in a view that can be yielded in a layout using YieldContent. This is useful for injecting scripts, styles, or sidebars from specific views.

View (e.g., users/index.etlua)
<% ContentFor("scripts", function() return [[
  <script>
    console.log("Page specific script");
  </script>
]] end) %>
Layout (e.g., layouts/app/index.html.etlua)
<body>
  ...
  <%- YieldContent("scripts") %>
</body>

data_object Passing Data

Controllers pass data to views through Page() function parameters.

Controller

function app.show()
  -- Page(view, layout, view_data, layout_data)
  Page("users/profile", "app", {
    -- Available in View
    user = { name = "John" },
    tab = "details"
  }, {
    -- Available in Layout
    title = "User Profile"
  })
end

View & Layout

<!-- In View (users/profile.etlua) -->
<h2>Hello, <%= user.name %>!</h2>
<p>Tab: <%= tab %></p>

<!-- In Layout (layouts/app/index.html.etlua) -->
<title><%= title %></title>

smartphone Device-Specific Views

Lua on Beans supports device-specific templates with the +device suffix. Device detection is configured in .init.lua.

settings

Configure Device Detection

.init.lua

function SetDevice()
  local user_agent = GetHeader("User-Agent")

  Params.request = { variant = "" }

  local preg = assert(re.compile("iPhone"))

  if preg:search(user_agent) then
    Params.request.variant = "iphone"
  end
end

info How it works:

check_circle SetDevice() is called on each request to detect the device
check_circle Params.request.variant determines which view suffix to use
check_circle You can add more device types (tablet, android, etc.) with additional regex patterns
folder

View File Structure

Name templates with +variant suffix

app/views/welcome/
├── index.etlua           # Default view (variant = "")
└── index+iphone.etlua    # iPhone view (variant = "iphone")

When Params.request.variant is set to "iphone", Lua on Beans serves index+iphone.etlua if it exists, otherwise falls back to index.etlua.

build Helper Functions

Built-in helper functions available in all templates:

Function Description Example
Partial(name, params) Include a partial template with optional parameters Partial("header", { title = "Home" })
PublicPath(path) Generate public asset URL PublicPath("/app.css")
RefreshPageForDevMode() Enable live reload in development RefreshPageForDevMode()
os.date(format) Standard Lua date formatting os.date("%Y")
ContentFor(tag, block) Define content block for layout ContentFor("head", fn)
YieldContent(tag) Render defined content block YieldContent("head")
lightbulb

Pro Tips

File naming: Use .etlua for content views and .html.etlua for layouts and partials.

Security: Always use <%= %> for user data to prevent XSS attacks. Only use <%- %> for trusted content.

Organization: Group related views in folders matching controller names (e.g., views/users/ for users_controller).