Template Manifest Reference
Every template must include a template.manifest.json file at its root. This file describes your template to the Gymlium platform and marketplace.
Full example
{
"id": "my-awesome-template",
"name": "My Awesome Template",
"version": "1.0.0",
"description": "A clean, modern design with bold typography and smooth animations.",
"author": {
"id": "dev-abc123",
"name": "Jane Developer",
"url": "https://janedev.com"
},
"category": "bold",
"tags": ["modern", "animation", "gradient"],
"preview": {
"thumbnail": "preview/thumbnail.png",
"screenshots": [
"preview/screenshot-home.png",
"preview/screenshot-about.png",
"preview/screenshot-mobile.png"
]
},
"pricing": {
"type": "standard",
"standard_price": 29.99,
"currency": "GBP"
},
"structure": {
"styles": "template.css",
"barrel": "index.ts",
"pages": {
"home": "pages/HomeContent.tsx",
"about": "pages/AboutContent.tsx",
"login": "pages/LoginContent.tsx"
},
"components": {
"header": "components/Header.tsx",
"footer": "components/Footer.tsx",
"loading": "components/Loading.tsx"
}
},
"navZones": [
{ "id": "main", "label": "Main Navigation" },
{ "id": "footer", "label": "Footer Links" }
],
"i18n": {
"rtlSupported": true,
"testedLocales": ["en", "ar", "fr", "de"]
}
}The manifest accepts only the fields listed below. The full schema is sdk/template.schema.json in the SDK.
Field reference
Required fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier. Lowercase, hyphens only. Must match your directory name. |
name | string | Display name shown in the marketplace. 2 to 60 characters. |
version | string | Semantic version (major.minor.patch). Start at 1.0.0. |
description | string | Short description for the marketplace listing. 10 to 500 characters. |
author | object | Author information (see below). |
category | string | One of: bold, elegant, minimalist, vibrant, wellness. |
tags | string[] | 1 to 10 searchable tags for marketplace discovery, each up to 30 characters. |
preview | object | Thumbnail and screenshots (see below). |
pricing | object | Pricing configuration (see below). |
structure | object | Paths to your stylesheet, barrel file, pages and components (see below). |
navZones | object[] | Navigation areas trainers can fill with links (see below). |
i18n | object | Internationalisation settings (see below). |
Optional fields
| Field | Type | Description |
|---|---|---|
branding | object | How your template handles trainer logos and the favicon (see below). |
imageSlots | object[] | The photo spots your template shows (see below). |
Author object
{
"id": "dev-abc123",
"name": "Jane Developer",
"url": "https://janedev.com"
}| Field | Required | Description |
|---|---|---|
id | Yes | Your developer ID from the Gymlium developer portal. |
name | Yes | Your display name on the marketplace. |
url | No | Link to your portfolio or website. Shown on your marketplace profile. |
Category values
| Category | Description |
|---|---|
bold | Strong visual impact, large typography, dramatic layouts |
elegant | Refined aesthetics, serif fonts, subtle animations |
minimalist | Clean, whitespace-focused, distraction-free |
vibrant | Colourful, energetic, gradient-heavy |
wellness | Calming, nature-inspired, soft colour palettes |
Choose the category that best describes your template’s visual personality. This affects how trainers discover your template in the marketplace.
Preview object
{
"thumbnail": "preview/thumbnail.png",
"screenshots": ["preview/screenshot-home.png", "preview/screenshot-mobile.png"]
}| Field | Required | Description |
|---|---|---|
thumbnail | Yes | Relative path to the marketplace thumbnail. The file must exist. |
screenshots | Yes | Relative paths to at least one screenshot for the template detail page. |
See Creating Preview Images for sizes and what to capture.
Pricing object
{
"type": "standard",
"standard_price": 29.99,
"currency": "GBP"
}| Field | Required | Description |
|---|---|---|
type | Yes | One of: free, free_with_attribution, standard, exclusive |
standard_price | For standard | Price of a standard licence. |
exclusive_price | For exclusive | Price of an exclusive licence (the template is delisted after purchase). |
attribution_removal_price | For free_with_attribution | One-time fee a trainer pays to remove the attribution link. |
currency | No | ISO 4217 currency code, e.g. "GBP". Defaults to "USD". |
Pricing types explained:
| Type | Description |
|---|---|
free | No cost, no restrictions. |
free_with_attribution | Free to use, but includes a “Powered by [your name]” footer link. Trainers can pay a one-time fee to remove it. |
standard | Paid template. Multiple trainers can purchase and use it. |
exclusive | Paid template. Only one trainer can buy it — then it’s delisted. Premium pricing expected. |
See Pricing Your Template for strategy advice.
Structure object
{
"styles": "template.css",
"barrel": "index.ts",
"pages": {
"home": "pages/HomeContent.tsx",
"about": "pages/AboutContent.tsx",
"login": "pages/LoginContent.tsx"
},
"components": {
"header": "components/Header.tsx",
"footer": "components/Footer.tsx",
"loading": "components/Loading.tsx"
}
}| Field | Required | Description |
|---|---|---|
styles | Yes | Path to the main template stylesheet. |
barrel | Yes | Path to the barrel export file. |
pages | Yes | Map of page keys to file paths. Must include home, about and login. |
components | Yes | Map of component keys to file paths. Must include header, footer and loading. |
Every path must point to a file that exists in your template.
navZones array
[
{ "id": "main", "label": "Main Navigation" },
{ "id": "footer", "label": "Footer Links" }
]At least one zone. Each needs a unique id and a human-readable label.
i18n object
{
"rtlSupported": true,
"testedLocales": ["en", "ar", "fr", "de"]
}| Field | Required | Description |
|---|---|---|
rtlSupported | Yes | Must be true. All templates must support RTL layouts. |
testedLocales | Yes | Array of locale codes you’ve tested. Must include at least "en". |
notes | No | Notes about i18n support or known limitations. |
rtlSupported must be true. Templates that don’t support RTL will be rejected during review. See the RTL Layout Guide for implementation details.
Available locale codes:
en, ar, fa, fr, de, es, pt, it, nl, tr, ru, zh, ja, ko
branding object (optional)
Declares how your template integrates with per-trainer branding (logos and favicon) configured in Gymlium:
{
"branding": {
"logo": {
"supported": true,
"headerHeightPx": 60,
"footerHeightPx": 36,
"fallback": "wordmark"
},
"favicon": {
"supported": true
}
}
}| Field | Required | Description |
|---|---|---|
logo.supported | Yes (if branding present) | Set true when Header/Footer use TemplateLogo. |
logo.headerHeightPx | No | Design target for header logo height (platform default 60). |
logo.footerHeightPx | No | Design target for footer logo height (platform default 36). |
logo.fallback | No | Must be "wordmark" — text of the trainer’s business name when no logo is uploaded. |
favicon.supported | Yes (if favicon present) | Set true to confirm your template is favicon-aware. |
Favicons are handled by the platform. Trainers can upload an optional favicon in the signup wizard and in Dashboard → Appearance. Gymlium injects the trainer’s favicon (or the Gymlium default when none is set) into the document <head> automatically for every page. Your template must not ship its own favicon.ico or hard-code a <link rel="icon"> tag — doing so would compete with the trainer’s choice. Declaring favicon.supported: true simply confirms your template follows this rule.
imageSlots array (optional)
Lists the photo spots your template shows. During signup, Gymlium previews each spot at its shape, in light and dark, and a new site seeds it with a sample photo or the trainer’s own upload. Leave imageSlots out if your template shows no photos; the validator then warns that signup cannot preview them.
{
"imageSlots": [
{ "id": "home-hero", "page": "home", "section": "home-hero", "field": "image_url", "ratio": "4:3" },
{ "id": "home-about-teaser", "page": "home", "section": "home-about-teaser", "field": "image_url", "ratio": "4:5", "persona": true },
{ "id": "about-hero", "page": "about", "section": "hero", "field": "image_url", "ratio": "3:1" },
{ "id": "about-image-section", "page": "about", "section": "image-section", "field": "image_url", "ratio": "3:4", "persona": true }
]
}| Field | Required | Description |
|---|---|---|
id | Yes | Sample photo slot the spot is seeded from: home-hero, home-about-teaser, about-hero or about-image-section. Each id may appear once. |
page | Yes | Page slug the spot appears on, e.g. home or about. |
section | Yes | Page section type that holds the image, e.g. home-hero or image-section. |
field | Yes | Image field in that section’s data, e.g. image_url. Its dark version is stored as image_url_dark. |
ratio | Yes | Shape your template crops the image to, as width:height (e.g. 16:10). Trainers crop uploads to this shape. |
persona | No | true when the sample photo follows the trainer’s “Sample photos show” choice (woman, man or no faces). |
The two banners (home-hero and about-hero) always keep a generic sample photo when a trainer picks “Your own” at signup. The trainer’s photo goes into the spots about them (home-about-teaser and about-image-section), and each spot can still be changed on its own.
Validation
The manifest is validated against a JSON Schema during npm run validate. Common validation errors:
- Missing required field — Ensure all required fields are present
- Invalid category — Must be one of the five allowed values
- Invalid pricing type — Must be one of the four allowed values
- rtlSupported is false — Must be
true - Version format — Must be valid semver (e.g.
1.0.0, not1.0) - ID mismatch — The
idfield must match the template directory name