Script Blocks
Script blocks in LUAT templates allow you to write Lua code that executes during template compilation and rendering. There are two types of script blocks, each with different execution contexts and purposes.
Types of Script Blocks
Regular Script Blocks
Regular <script>
blocks execute on every template render. Use them for:
- Importing components and modules
- Preparing data for rendering
- Computing derived values
- Setting up local variables
<script>
local Helper = require("lib/utils/helper")
local Button = require("components/Button")
local title = props.title or "Default Title"
local isActive = props.status === "active"
local formattedDate = Helper.formatDate(props.createdAt)
</script>
<div class="card {isActive and 'active' or ''}">
<h2>{title}</h2>
<p>Created: {formattedDate}</p>
<Button>Edit</Button>
</div>
Module Script Blocks
Module script blocks (<script module>
) execute once when the module is first loaded. Use them for:
- Exporting module metadata and utilities
- Defining constants and configuration
- Setting up module-level functions
<script module>
local type = "component:user-card"
local function validateUser(user)
return user and user.name and user.email
end
exports.type = type
exports.validateUser = validateUser
</script>
<script>
local user = props.user
local isValid = validateUser(user)
</script>
{#if isValid}
<div class="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
{:else}
<div class="error">Invalid user data</div>
{/if}
Importing Modules
Component Imports
Import other LUAT components using require()
:
<script>
local Card = require("components/Card")
local Button = require("components/Button")
local Icon = require("components/Icon")
</script>
<Card title="User Profile">
<Icon name="user" />
<Button variant="primary">Edit Profile</Button>
</Card>
Utility Imports
Import Lua modules and utilities:
<script>
local json = require("json")
local helper = require("lib/utils/helper")
local dateUtils = require("lib/utils/date")
local userData = json.encode(props.user)
local avatarUrl = helper.resolveAsset(props.user.avatar)
local formattedDate = dateUtils.format(props.user.createdAt, "MMM DD, YYYY")
</script>
Context Access
Getting Context Data
Access shared context using the getContext()
function:
<script>
local pageContext = getContext("pageContext")
local currentNode = getContext("currentNode")
local currentTheme = pageContext.currentTheme
local cssUrl = pageContext.root_path .. "/public/bundle/app.css"
local title = currentNode.properties.title
</script>
<div data-theme={currentTheme}>
<h1>{title}</h1>
<link rel="stylesheet" href={cssUrl}>
</div>
Available Context Variables
Common context variables include:
pageContext
- Page-level configuration and URLscurrentNode
- Current content node data and propertiescurrentTheme
- Active theme settings
<script>
local pageContext = getContext("pageContext")
local currentNode = getContext("currentNode")
-- Page context properties
local rootPath = pageContext.root_path
local isDev = pageContext.query.dev
local isPreview = pageContext.query.preview
-- Current node properties
local nodeTitle = currentNode.properties.title
local nodeType = currentNode.content_type
local nodeName = currentNode.name
</script>
Working with Props
Accessing Props
All props passed to the component are available in the global props
table:
<script>
local title = props.title
local subtitle = props.subtitle
local image = props.image
local items = props.items or {}
local showActions = props.showActions or false
</script>
Derived Values
Compute derived values from props in script blocks:
<script>
local user = props.user
local isAdmin = user and user.role === "admin"
local displayName = user and (user.displayName or user.name) or "Guest"
local avatarUrl = user and user.avatar or "/default-avatar.png"
local itemCount = props.items and #props.items or 0
local hasItems = itemCount > 0
</script>
<div class="user-info">
<img src={avatarUrl} alt={displayName}>
<h2>{displayName}</h2>
{#if isAdmin}
<span class="admin-badge">Admin</span>
{/if}
<p>{itemCount} items</p>
</div>
Advanced Patterns
Conditional Imports
Dynamically import components based on conditions:
<script>
local componentType = props.type or "default"
local Component
if componentType === "hero" then
Component = require("components/Hero")
elseif componentType === "gallery" then
Component = require("components/Gallery")
else
Component = require("components/DefaultComponent")
end
</script>
<Component {...props} />
Helper Functions
Define utility functions in script blocks:
<script>
local function formatPrice(price)
return string.format("$%.2f", price)
end
local function getStatusClass(status)
if status === "active" then
return "status-active"
elseif status === "pending" then
return "status-pending"
else
return "status-inactive"
end
end
local price = formatPrice(props.price)
local statusClass = getStatusClass(props.status)
</script>
<div class="product {statusClass}">
<h3>{props.name}</h3>
<p class="price">{price}</p>
</div>
Error Handling
Handle errors gracefully in script blocks:
<script>
local user = props.user
local hasValidUser = user and user.name and user.email
local function safeAccess(obj, path)
local current = obj
for key in string.gmatch(path, "([^.]+)") do
if current and type(current) === "table" then
current = current[key]
else
return nil
end
end
return current
end
local profileImage = safeAccess(user, "profile.avatar.url")
local displayName = safeAccess(user, "profile.displayName") or
safeAccess(user, "name") or "Unknown User"
</script>
{#if hasValidUser}
<div class="user-profile">
{#if profileImage}
<img src={profileImage} alt={displayName}>
{/if}
<h2>{displayName}</h2>
</div>
{:else}
<div class="error">Invalid user data</div>
{/if}
Best Practices
1. Keep script blocks focused
Separate concerns between module and regular scripts:
<!-- Module script: exports and constants -->
<script module>
local TYPE = "component:product-card"
local DEFAULT_CURRENCY = "USD"
exports.type = TYPE
exports.currency = DEFAULT_CURRENCY
</script>
<!-- Regular script: rendering logic -->
<script>
local price = props.price
local currency = props.currency or DEFAULT_CURRENCY
local formattedPrice = formatCurrency(price, currency)
</script>
2. Use meaningful variable names
<script>
-- Good: descriptive names
local userName = props.user.name
local isLoggedIn = props.user ~= nil
local itemCount = #props.items
-- Avoid: cryptic abbreviations
local un = props.user.name
local logged = props.user ~= nil
local cnt = #props.items
</script>
3. Handle edge cases
<script>
local items = props.items or {}
local user = props.user
-- Safe property access
local userName = user and user.name or "Guest"
local userEmail = user and user.email or ""
-- Safe array operations
local firstItem = items[1]
local itemCount = #items
local hasItems = itemCount > 0
</script>
4. Extract complex logic
Move complex logic to utility modules:
<!-- Good: Clean script block -->
<script>
local UserHelper = require("lib/helpers/UserHelper")
local user = props.user
local userDisplayData = UserHelper.prepareUserDisplay(user)
local permissions = UserHelper.getUserPermissions(user)
</script>
<!-- Instead of cluttering the script with complex logic -->