Reference
Data Pages
The engine reads files in
data/and exposes them asdata.<filename>variables in every template. JSON-LD structured data for collection-style records is auto-generated for SEO.
Store structured content in data/*.json files. Every file is available in every template as {{ data.filename }} — no configuration needed.
data/faq.json → {{ data.faq }}
data/team.json → {{ data.team }}
data/menu.json → {{ data.menu }}
data/testimonials.json → {{ data.testimonials }}
data/products.json → {{ data.products }}
Pattern
1. Create the data file in data/
2. Loop over it in the page template
3. The engine auto-generates matching JSON-LD schema for known types (FAQ, HowTo, Product)
FAQ
The engine auto-generates FAQPage JSON-LD schema when it detects FAQ data — good for Google rich results.
// data/faq.json
[
{
"question": "How long does a typical project take?",
"answer": "Most projects are completed within 2–4 weeks depending on scope and feedback cycles."
},
{
"question": "Do you offer revisions?",
"answer": "Yes — all packages include two rounds of revisions."
}
]
<!-- In pages/faq.html or as a section in any page -->
<section class="max-w-3xl mx-auto px-6 py-16">
<h2 class="text-3xl font-bold mb-10">Frequently Asked Questions</h2>
<div class="space-y-6">
{% for item in data.faq %}
<div x-data="{ open: false }" class="border-b border-secondary-200 pb-6">
<button @click="open = !open" class="flex justify-between items-center w-full text-left font-semibold text-lg">
{{ item.question }}
<span x-text="open ? '−' : '+'"></span>
</button>
<div x-show="open" x-cloak class="mt-3 text-secondary-600">{{ item.answer }}</div>
</div>
{% endfor %}
</div>
</section>
Team
// data/team.json
[
{
"name": "Sarah Chen",
"role": "Founder & CEO",
"bio": "15 years in product design, previously at Figma and Stripe.",
"image": "https://images.unsplash.com/photo-1494790108755-2616b612b77c?w=400&q=80",
"linkedin": "https://linkedin.com/in/sarahchen"
},
{
"name": "Marcus Webb",
"role": "Lead Engineer",
"bio": "Full-stack engineer with a focus on performance and developer experience.",
"image": "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&q=80"
}
]
<div class="grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{% for person in data.team %}
<div class="text-center">
<img src="{{ person.image }}" alt="{{ person.name }}" class="w-24 h-24 rounded-full object-cover mx-auto mb-4">
<h3 class="font-semibold text-lg">{{ person.name }}</h3>
<p class="text-primary-600 text-sm mb-2">{{ person.role }}</p>
<p class="text-secondary-600 text-sm">{{ person.bio }}</p>
{% if person.linkedin %}
<a href="{{ person.linkedin }}" class="text-primary-600 text-sm mt-2 inline-block">LinkedIn →</a>
{% endif %}
</div>
{% endfor %}
</div>
Testimonials
// data/testimonials.json
[
{
"quote": "Working with them transformed our online presence. Revenue up 40% in three months.",
"author": "Jessica Park",
"title": "CEO, Bloom Skincare",
"image": "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&q=80",
"rating": 5
}
]
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{% for t in data.testimonials %}
<blockquote class="bg-white rounded-xl p-6 shadow-sm">
{% if t.rating %}
<div class="text-yellow-400 mb-3">{% for i in (1..t.rating) %}★{% endfor %}</div>
{% endif %}
<p class="text-secondary-700 mb-4">"{{ t.quote }}"</p>
<footer class="flex items-center gap-3">
{% if t.image %}<img src="{{ t.image }}" alt="{{ t.author }}" class="w-10 h-10 rounded-full object-cover">{% endif %}
<div>
<div class="font-semibold text-sm">{{ t.author }}</div>
{% if t.title %}<div class="text-secondary-500 text-xs">{{ t.title }}</div>{% endif %}
</div>
</footer>
</blockquote>
{% endfor %}
</div>
Pricing
// data/pricing.json
[
{
"name": "Starter",
"price": "49",
"period": "month",
"description": "Perfect for small businesses just getting started.",
"features": ["5 pages", "Contact form", "SEO basics", "SSL included"],
"cta": "Get started",
"ctaUrl": "/contact",
"featured": false
},
{
"name": "Pro",
"price": "99",
"period": "month",
"description": "Everything you need to grow.",
"features": ["Unlimited pages", "Blog", "Analytics", "Priority support", "Custom domain"],
"cta": "Start free trial",
"ctaUrl": "/signup",
"featured": true
}
]
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{% for plan in data.pricing %}
<div class="rounded-2xl p-8 {% if plan.featured %}bg-primary-600 text-white ring-2 ring-primary-600{% else %}bg-white border border-secondary-200{% endif %}">
<h3 class="text-xl font-bold mb-1">{{ plan.name }}</h3>
<div class="flex items-baseline gap-1 my-4">
<span class="text-4xl font-bold">${{ plan.price }}</span>
<span class="{% if plan.featured %}text-primary-200{% else %}text-secondary-500{% endif %}">/ {{ plan.period }}</span>
</div>
<p class="{% if plan.featured %}text-primary-100{% else %}text-secondary-600{% endif %} mb-6">{{ plan.description }}</p>
<ul class="space-y-3 mb-8">
{% for feature in plan.features %}
<li class="flex items-center gap-2 text-sm">
<span>✓</span> {{ feature }}
</li>
{% endfor %}
</ul>
<a href="{{ plan.ctaUrl }}" class="block text-center py-3 px-6 rounded-xl font-semibold {% if plan.featured %}bg-white text-primary-600 hover:bg-primary-50{% else %}bg-primary-600 text-white hover:bg-primary-700{% endif %}">
{{ plan.cta }}
</a>
</div>
{% endfor %}
</div>
Navigation / Menu
// data/menu.json
[
{ "label": "Home", "url": "/" },
{ "label": "Services", "url": "/services" },
{ "label": "About", "url": "/about" },
{ "label": "Blog", "url": "/blog" },
{ "label": "Contact", "url": "/contact" }
]
<nav>
{% for item in data.menu %}
<a href="{{ item.url }}"
class="{% if currentPath == item.url %}text-primary-600 font-semibold{% else %}text-secondary-700 hover:text-primary-600{% endif %}">
{{ item.label }}
</a>
{% endfor %}
</nav>
Rules
- Data files go in
data/— accessed as{{ data.filename }}(without.json) - Data is available on every page — no need to import or configure per page
- Keep data files as flat arrays of objects when possible — easier to loop
featuresarrays inside pricing/service objects must be JSON arrays:["item1", "item2"]- For FAQ data, the engine auto-generates
FAQPageschema — don't add it manually - Use
{% if item.field %}...{% endif %}guards for optional fields so missing data doesn't break the layout