Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

First template pack

Build a working template pack from scratch.

1. Lay out the directory#

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
my-pack/
  manifest.json
  templates/
    base.html
    page.html
    blog-post.html
    blog-list.html
    home.html
  assets/
    css/styles.css

2. manifest.json#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "name": "My Pack",
  "slug": "my-pack",
  "version": "1.0.0",
  "templates": [
    {"slug": "base",        "file": "templates/base.html",        "type": "layout"},
    {"slug": "page",        "file": "templates/page.html",        "type": "page", "content_types": ["page"]},
    {"slug": "blog-post",   "file": "templates/blog-post.html",   "type": "page", "content_types": ["blog-post"]},
    {"slug": "blog-list",   "file": "templates/blog-list.html",   "type": "page"},
    {"slug": "home",        "file": "templates/home.html",        "type": "page"}
  ]
}

3. base.html#

jinja
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="{{ locale | default(site.default_locale | default('en')) }}">
<head>
  <meta charset="UTF-8">
  <title>{% block title %}{{ site.name }}{% endblock %}</title>
  <link rel="stylesheet" href="{{ asset_url('css/styles.css') }}">
</head>
<body>
  <main>{% block content %}{% endblock %}</main>
</body>
</html>

4. blog-post.html#

jinja
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{% extends "base.html" %}
{% block title %}{{ content.fields.title }}{% endblock %}
{% block content %}
<article>
  <h1>{{ content.fields.title }}</h1>
  <time datetime="{{ content.fields.published_at }}">
    {{ content.fields.published_at | format_date }}
  </time>
  {% if content.fields.hero_image %}
    <img src="{{ asset_url(content.fields.hero_image, 'large') }}" alt="">
  {% endif %}
  <div class="prose">{{ content.fields.body | markdown }}</div>
</article>
{% endblock %}

5. Verify, package, upload#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
python -m scaicms.cli.template_packs verify ./my-pack --check-syntax
python -m scaicms.cli.template_packs package ./my-pack -o /tmp/my-pack.zip

curl -X POST \
  "http://localhost:8000/api/v1/template-packs/upload?set_as_default=true&replace_existing=true" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Site-ID: $SITE" \
  -F "file=@/tmp/my-pack.zip"

redis-cli PUBLISH scaicms:cache:invalidate "site:$SITE:*"

6. View#

text
1
http://localhost:8080/blog/your-first-post

If you don't see your changes, check the delivery service logs — Jinja errors surface there. The most common gotcha: writing content.fields.items (Python's dict method!) instead of content.fields["items"] for bracket-notation access.

What's next#

  • Add a blocks field type and declare block_types in your manifest.
  • Build template-pack partials (templates/partials/header.html, etc.) for reuse with {% include %}.
  • Add a manifest.json → settings block to let site admins toggle behavior without code changes.
Updated 2026-05-16 12:33:52 View source (.md) rev 2