Theme Development
Template Variables

Template Variables

Every template has access to common variables plus page-specific data.

Global Variables

Available in all templates:

VariableTypeDescription
sdkObjectFull SDK access for content, media, utils
settingsObjectSite settings
activeThemeStringCurrent theme ID
hooksObjectHook system for plugins
__FunctionTranslation function

Settings Object

settings = {
  siteName: "My Website",
  siteDescription: "Site description",
  siteUrl: "https://example.com",
  logo: "/uploads/logo.png",
  favicon: "/uploads/favicon.ico",
  language: "en",
  direction: "ltr",
  timezone: "America/New_York",
  dateFormat: "MMMM D, YYYY",
  postsPerPage: 12,
  
  // Social links
  social: {
    facebook: "https://facebook.com/...",
    twitter: "https://twitter.com/...",
    instagram: "https://instagram.com/...",
    youtube: "https://youtube.com/..."
  },
  
  // Contact info
  contact: {
    email: "[email protected]",
    phone: "+1 234 567 890",
    address: "123 Main St"
  }
}

Page-Specific Variables

Homepage (home.html)

// Latest posts
latestPosts = [
  { id, title, slug, url, excerpt, featuredImage, createdAt, author, category },
  ...
]
 
// Featured posts
featuredPosts = [...]
 
// Categories with images
categories = [
  { id, name, slug, url, image, postCount },
  ...
]
 
// Homepage sections (from admin builder)
sections = [
  { type: 'hero', heroTitle, heroSubtitle, heroImage, ... },
  { type: 'posts', title, posts, columns, showViewAll, ... },
  { type: 'categories', title, categories, ... },
  ...
]
 
// Top tags
topTags = [{ id, name, slug, url, postCount }, ...]

Example:

home.html
{# Hero section #}
{% if (sections) { %}
  {% sections.forEach(section => { %}
    {% if (section.type === 'hero') { %}
      <section class="hero">
        <h1>{{ section.heroTitle }}</h1>
        <p>{{ section.heroSubtitle }}</p>
      </section>
    {% } %}
  {% }) %}
{% } %}
 
{# Latest posts #}
{% if (latestPosts && latestPosts.length > 0) { %}
  <section class="latest">
    <h2>Latest Posts</h2>
    {% latestPosts.forEach(post => { %}
      {{{ include('partials/post-card', { post }) }}}
    {% }) %}
  </section>
{% } %}

Single Post (post-detail.html)

// The post
post = {
  id: "...",
  title: "Post Title",
  slug: "post-title",
  url: "/posts/post-title",
  content: "<p>HTML content...</p>",
  excerpt: "Short excerpt...",
  featuredImage: "/uploads/image.jpg",
  featuredImageData: { /* responsive image data */ },
  
  createdAt: "2026-01-15T...",
  updatedAt: "2026-01-16T...",
  publishedAt: "2026-01-15T...",
  
  author: { id, name, slug, url, avatar, bio },
  category: { id, name, slug, url },
  tags: [{ id, name, slug, url }, ...],
  
  // SEO
  seoTitle: "Custom SEO title",
  seoDescription: "Custom meta description",
  seoKeywords: ["keyword1", "keyword2"],
  
  // Structured data fields (if applicable)
  prepTime: "15 minutes",
  cookTime: "30 minutes",
  servings: 4,
  // ... other custom fields
}
 
// Related posts
relatedPosts = [...]
 
// Previous/Next posts
prevPost = { id, title, url, featuredImage }
nextPost = { id, title, url, featuredImage }

Example:

post-detail.html
<article class="post">
  {# Header #}
  <header>
    {% if (post.category) { %}
      <a href="{{ post.category.url }}" class="category">
        {{ post.category.name }}
      </a>
    {% } %}
    
    <h1>{{ post.title }}</h1>
    
    <div class="meta">
      <time>{{ sdk.utils.formatDate(post.createdAt) }}</time>
      {% if (post.author) { %}
        <span>by {{ post.author.name }}</span>
      {% } %}
    </div>
  </header>
  
  {# Featured image #}
  {% if (post.featuredImage) { %}
    <img src="{{ post.featuredImage }}" alt="{{ post.title }}">
  {% } %}
  
  {# Content #}
  <div class="content">
    {{{ post.content }}}
  </div>
  
  {# Tags #}
  {% if (post.tags && post.tags.length > 0) { %}
    <div class="tags">
      {% post.tags.forEach(tag => { %}
        <a href="{{ tag.url }}">{{ tag.name }}</a>
      {% }) %}
    </div>
  {% } %}
  
  {# Author box #}
  {% if (post.author) { %}
    {{{ include('partials/author-box', { author: post.author }) }}}
  {% } %}
  
  {# Related posts #}
  {% if (relatedPosts && relatedPosts.length > 0) { %}
    <section class="related">
      <h3>Related Posts</h3>
      {% relatedPosts.forEach(related => { %}
        {{{ include('partials/post-card', { post: related }) }}}
      {% }) %}
    </section>
  {% } %}
</article>

Posts Archive (posts.html)

// Posts list
posts = [{ id, title, slug, url, excerpt, featuredImage, createdAt, author, category }, ...]
 
// Pagination
pagination = {
  page: 1,
  pages: 10,
  total: 120,
  limit: 12,
  hasNext: true,
  hasPrev: false
}

Example:

posts.html
<div class="posts-archive">
  <h1>All Posts</h1>
  
  {% if (posts && posts.length > 0) { %}
    <div class="posts-grid">
      {% posts.forEach(post => { %}
        {{{ include('partials/post-card', { post }) }}}
      {% }) %}
    </div>
    
    {# Pagination #}
    {{{ sdk.ui.pagination({
      currentPage: pagination.page,
      totalPages: pagination.pages,
      baseUrl: '/posts'
    }) }}}
  {% } else { %}
    <p>No posts found.</p>
  {% } %}
</div>

Category Page (category-detail.html)

// The category
category = {
  id: "...",
  name: "Category Name",
  slug: "category-name",
  url: "/categories/category-name",
  description: "Category description",
  image: "/uploads/category.jpg",
  postCount: 25
}
 
// Posts in category
posts = [...]
 
// Pagination
pagination = { ... }

Author Page (author-detail.html)

// The author
author = {
  id: "...",
  name: "Author Name",
  slug: "author-name",
  url: "/authors/author-name",
  email: "[email protected]",
  bio: "Author biography...",
  avatar: "/uploads/avatar.jpg",
  social: {
    website: "https://...",
    twitter: "@handle",
    instagram: "@handle"
  },
  postCount: 42
}
 
// Author's posts
posts = [...]
 
// Pagination
pagination = { ... }

Search Page (search.html)

// Search query
query = "search term"
 
// Search results
results = [{ id, title, url, excerpt, featuredImage, type }, ...]
 
// Pagination
pagination = { ... }

Example:

search.html
<div class="search-page">
  <h1>Search Results</h1>
  
  <form action="/search" method="get" class="search-form">
    <input type="text" name="q" value="{{ query }}" placeholder="Search...">
    <button type="submit">Search</button>
  </form>
  
  {% if (query) { %}
    <p>Results for "{{ query }}"</p>
    
    {% if (results && results.length > 0) { %}
      <div class="results">
        {% results.forEach(item => { %}
          <article>
            <h3><a href="{{ item.url }}">{{ item.title }}</a></h3>
            <p>{{ item.excerpt }}</p>
          </article>
        {% }) %}
      </div>
      
      {{{ sdk.ui.pagination({
        currentPage: pagination.page,
        totalPages: pagination.pages,
        baseUrl: '/search?q=' + encodeURIComponent(query)
      }) }}}
    {% } else { %}
      <p>No results found.</p>
    {% } %}
  {% } %}
</div>

Error Page (error.html)

statusCode = 404  // or 500, etc.
message = "Page not found"

Example:

error.html
<div class="error-page">
  <h1>{{ statusCode }}</h1>
  <p>{{ message }}</p>
  <a href="/">Go Home</a>
</div>

Accessing Custom Fields

Posts can have custom fields defined in the admin:

{# Access custom fields #}
{% if (post.customFields) { %}
  <div class="custom-data">
    <span>Prep Time: {{ post.customFields.prepTime }}</span>
    <span>Cook Time: {{ post.customFields.cookTime }}</span>
    <span>Servings: {{ post.customFields.servings }}</span>
  </div>
{% } %}

Next Steps