Skip to main content

Content Types

Wunderframe Applications use a flexible content type system that allows you to define different page types and their associated templates. Content types determine how different kinds of content are rendered and what fields are available for editing.

What are Content Types?

Content types are Lua modules that define:

  • Template rendering logic - How the content appears on the page
  • Field definitions - What data can be edited
  • Validation rules - How to ensure content is valid
  • Default values - Starting content for new pages

Built-in Content Types

Wunderframe comes with several pre-built content types:

page - Basic Page

The default content type for simple pages with rich text content.

-- lib/content_types/page.lua
local luat = require("luat")

local function render(node, runtime)
return luat.compile("lib/pages/page.luat", {
title = node.properties.title or "Untitled Page",
content = node.properties.content or "",
node = node
}, runtime)
end

return {
render = render,
fields = {
{
name = "title",
type = "text",
label = "Page Title",
required = true
},
{
name = "content",
type = "richtext",
label = "Page Content"
}
}
}

homepage - Landing Page

Specialized content type for site homepages with hero sections and feature areas.

article - Blog Post

Content type for blog posts with metadata, tags, and publishing features.

Content type for showcasing collections of images with captions.

Creating Custom Content Types

You can create custom content types by adding modules to the lib/content_types/ directory:

-- lib/content_types/product.lua
local luat = require("luat")

local function render(node, runtime)
local props = node.properties

return luat.compile("lib/pages/product.luat", {
name = props.name,
price = props.price,
description = props.description,
images = props.images or {},
node = node
}, runtime)
end

return {
render = render,
fields = {
{
name = "name",
type = "text",
label = "Product Name",
required = true
},
{
name = "price",
type = "number",
label = "Price ($)",
required = true
},
{
name = "description",
type = "richtext",
label = "Product Description"
},
{
name = "images",
type = "media",
label = "Product Images",
multiple = true
}
}
}

Content Type Registration

Content types are automatically discovered and registered based on the file structure in lib/content_types/. The filename becomes the content type identifier.

File Structure

lib/content_types/
├── page.lua # content_type: "page"
├── homepage.lua # content_type: "homepage"
├── article.lua # content_type: "article"
└── product.lua # content_type: "product"

Field Types

Wunderframe supports various field types for content editing:

Text Fields

{
name = "title",
type = "text",
label = "Title",
placeholder = "Enter title...",
required = true,
maxLength = 100
}

Rich Text

{
name = "content",
type = "richtext",
label = "Content",
toolbar = {"bold", "italic", "link", "heading"}
}

Media Upload

{
name = "image",
type = "media",
label = "Featured Image",
accept = "image/*",
multiple = false
}

Number Input

{
name = "price",
type = "number",
label = "Price",
min = 0,
step = 0.01
}

Select Dropdown

{
name = "category",
type = "select",
label = "Category",
options = {
{value = "tech", label = "Technology"},
{value = "design", label = "Design"},
{value = "business", label = "Business"}
}
}

Boolean Toggle

{
name = "featured",
type = "boolean",
label = "Featured Item",
default = false
}

Template Integration

Content types work seamlessly with LUAT templates. The render function passes data to templates which can access it using familiar syntax:

<!-- lib/pages/product.luat -->
<div class="product">
<h1>{name}</h1>
<p class="price">${price}</p>

<div class="description">
{content}
</div>

{#if images && #images > 0}
<div class="gallery">
{#each images as image}
<img src="{image.url}" alt="{image.alt}" />
{/each}
</div>
{/if}
</div>

Validation

Content types can include validation logic to ensure data integrity:

local function validate(properties)
local errors = {}

if not properties.name or properties.name == "" then
table.insert(errors, "Product name is required")
end

if properties.price and properties.price < 0 then
table.insert(errors, "Price must be positive")
end

return errors
end

return {
render = render,
validate = validate,
fields = {
-- field definitions
}
}

Advanced Features

Dynamic Fields

Fields can be conditionally shown based on other field values:

{
name = "sale_price",
type = "number",
label = "Sale Price",
showIf = function(props)
return props.on_sale == true
end
}

Field Groups

Organize related fields into collapsible groups:

fields = {
{
type = "group",
label = "SEO Settings",
fields = {
{name = "meta_title", type = "text", label = "Meta Title"},
{name = "meta_description", type = "text", label = "Meta Description"}
}
}
}

What's Next?