Skip to main content
SiteShi p

Reference

Data Pages

The engine reads files in data/ and exposes them as data.<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>

// 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
  • features arrays inside pricing/service objects must be JSON arrays: ["item1", "item2"]
  • For FAQ data, the engine auto-generates FAQPage schema — don't add it manually
  • Use {% if item.field %}...{% endif %} guards for optional fields so missing data doesn't break the layout

Found something out of date? Open an issue. ← All docs