Welcome to Fragmentor v4. Still working in v3?Switch to v3
  1. Docs
  2. Components

Components

A component is an HTML template that decides how an object of one type should be rendered visually.

Components are stored in /components/, one .html file each. The file name is the type it renders, optionally prefixed with its ancestors (e.g. Product.Faq.html matches a Faq nested inside a Product): the most specific match wins.

<!-- /components/Hero.html -->
<section>
  <h1 x-bind="heading"></h1>
</section>

Anatomy

A component is plain HTML. CSS and JS may live inside the component, in a <style>/<script> tag. Shared CSS/JS is linked into a component (<link rel="stylesheet" href="/main.css">) relative to the /assets/ folder (see Assets).

In order to accept props in a component, you must use custom html directives.

  • x-bind="field" replaces the element's inner content with the field's value, or (if the field is a nested object) injects that object's own matched component in its place.
  • x-bind:attr="field" replaces the element's attribute (like href or src) with the field's value.
  • x-if="condition" removes the element only if the condition evaluates false.
  • x-for="field" repeats the element for each object in the field.

Each directive's value is an expression, not full JavaScript. It evaluates a safe subset of it (field access, comparisons, &&/||/??, ternaries, method calls like .includes()).

Directives run in a fixed order: x-for, then x-if, then x-bind, then x-bind:attr. An element is repeated first, and only then are its conditions checked and its values filled in. When x-bind and x-for are on the same element, x-bind wins and x-for is ignored.

A nested component only sees its own fields. $ refers to the top-level document being rendered (usually the page), so any component can read its values: x-bind="$.site.language.checkout".

Example

Design a structured nested object that follows a given schema (defined by _type).

{
  "_type": "Features",
  "heading": "Why Ninjas Choose Us",
  "show": true,
  "features": [
    { "_type": "Feature", "title": "Stealth Mode", "body": "Deploys are invisible until they're not." },
    { "_type": "Feature", "title": "Free Katana", "body": "Every plan ships with one (not really)." }
  ],
  "links": [
    { "_type": "Link", "label": "Join the Dojo", "url": "/signup" }
  ]
}

Create a Features.html component and accept properties heading, show, features and links available in the Features schema. We can choose to embed components directly using x-for on links.

<!-- Features.html -->
<section>
  <h2 x-bind="heading"></h2>
  <div x-bind="features"></div>
  <div x-if="show" x-for="links">
    <a x-bind:href="url" x-bind="label"></a>
  </div>
</section>

Now create the nested Feature component and accept title and body.

<!-- Feature.html (Features.Feature.html would work here too) -->
<div class="feature">
  <h3 x-bind="title"></h3>
  <p x-bind="body"></p>
</div>

<style>
  .feature.closed p { display: none; }
</style>

<script>
  /* click a title to show or hide its body */
  for (const feature of document.querySelectorAll(".feature")) {
    feature.querySelector("h3").addEventListener("click", () => feature.classList.toggle("closed"));
  }
</script>

The build script turns the object into raw html output.

<link rel="stylesheet" href="/_styles/<hash>.css">

<section>
  <h2>Why Ninjas Choose Us</h2>
  <div>
    <div class="feature">
      <h3>Stealth Mode</h3>
      <p>Deploys are invisible until they're not.</p>
    </div>
    <div class="feature">
      <h3>Free Katana</h3>
      <p>Every plan ships with one (not really).</p>
    </div>
  </div>
  <div>
    <a href="/signup">Join the Dojo</a>
  </div>
</section>

<script type="module" src="/_scripts/<hash>.js"></script>