what

Components

Reusable UI building blocks. Write a plain HTML file, and it becomes a custom tag you can use anywhere.

Creating a Component

Any HTML file in the components/ directory becomes a component. The filename determines the tag name with a what- prefix:

FileTag
components/card.html<what-card>
components/user-card.html<what-user-card>
components/alert.html<what-alert>

A minimal component is just HTML:

components/greeting.html
<div class="greeting">
  <p>Hello from a component!</p>
</div>

Use it in any page:

<what-greeting></what-greeting>

Props

Declare props in a <what> block at the top of the component file. Props become template variables inside the component.

components/card.html
<what>
props = "title, subtitle"
defaults.title = "Untitled"
</what>

<div class="card">
  <div class="card-header">
    <h3>#title#</h3>
    <if subtitle>
      <p class="text-muted">#subtitle#</p>
    </if>
  </div>
  <div class="card-body">
    <slot/>
  </div>
</div>

Props with defaults are optional. Props without defaults render as empty if not provided.

Using Components

Pass prop values as attributes on the tag:

<what-card title="Team Members" subtitle="Active users">
  <p>Card content goes here.</p>
</what-card>

You can use template variables in prop values:

<what-card title="Welcome, #user.full_name#">
  <p>Your dashboard overview.</p>
</what-card>

Slots

The <slot/> tag marks where children content is injected. Everything between the opening and closing component tags replaces <slot/>.

components/section.html
<what>
props = "title"
</what>

<section class="p-6">
  <h2>#title#</h2>
  <div class="mt-4">
    <slot/>
  </div>
</section>
<what-section title="Features">
  <ul>
    <li>Fast server rendering</li>
    <li>Zero JavaScript by default</li>
    <li>File-based routing</li>
  </ul>
</what-section>

If a component has no <slot/> tag, children content is discarded.

JSON Array Props

Pass structured data as JSON in a prop attribute. Use single quotes around the JSON value:

components/feature-list.html
<what>
props = "features"
</what>

<ul class="space-y-2">
  <loop data="#features#" as="feature">
    <li>#feature.name# — #feature.description#</li>
  </loop>
</ul>
<what-feature-list features='[
  {"name": "Routing", "description": "File-based, automatic"},
  {"name": "Auth", "description": "JWT cookie, role-based"},
  {"name": "Forms", "description": "Validation and CRUD"}
]'></what-feature-list>
Tip: Use single quotes for the attribute and double quotes inside the JSON. The JSON is parsed as serde_json::Value, so standard JSON format is required.

Loops Inside Components

Components can loop over their own props data. This is how you build dynamic, data-driven components:

components/user-table.html
<what>
props = "users"
</what>

<div class="table-container">
  <table class="table table-hover">
    <thead>
      <tr><th>Name</th><th>Email</th></tr>
    </thead>
    <tbody>
      <loop data="#users#" as="u">
        <tr>
          <td>#u.name#</td>
          <td>#u.email#</td>
        </tr>
      </loop>
    </tbody>
  </table>
</div>

Loops also work with data fetched at the page level and passed down as a prop:

<!-- Page fetches data, component renders it -->
<what>
fetch.team = "local:users"
</what>

<what-user-table users="#team#"></what-user-table>

Nested Components

Components can use other components. They resolve in order, from the innermost outward:

components/dashboard.html
<what>
props = "title"
</what>

<div class="grid grid-cols-2 gap-4">
  <what-card title="#title#">
    <what-badge variant="badge-success">Active</what-badge>
    <slot/>
  </what-card>
</div>
Avoid circular nesting. Component A using component B which uses component A will cause an infinite loop.

Extending Components

Components become powerful when you combine props, defaults, conditionals, and loops. Here are common patterns.

Multiple Props with Defaults

Declare several props with sensible defaults. Callers only override what they need:

components/stat-card.html
<what>
props = "label, value, icon, color"
defaults.icon = ""
defaults.color = "indigo"
</what>

<div class="stat-card" style="border-left: 3px solid var(--color-#color#);">
  <if icon>
    <span class="stat-icon">#icon#</span>
  </if>
  <div class="stat-value">#value#</div>
  <div class="stat-label">#label#</div>
</div>
<!-- Minimal usage — defaults apply -->
<what-stat-card label="Users" value="1,024"></what-stat-card>

<!-- Override color and icon -->
<what-stat-card label="Revenue" value="$42k" icon="$" color="green"></what-stat-card>

Conditional Rendering Inside Components

Use <if> inside component templates to vary output based on props:

components/button.html
<what>
props = "variant, href"
defaults.variant = "primary"
defaults.href = ""
</what>

<if href>
  <a href="#href#" class="btn btn-#variant#"><slot/></a>
</if>
<unless href>
  <button class="btn btn-#variant#"><slot/></button>
</unless>

Loops Inside Components

Components can receive array data (as JSON or from a page fetch) and loop over it:

components/tag-list.html
<what>
props = "tags"
</what>

<div class="tag-list">
  <loop data="#tags#" as="tag">
    <span class="badge">#tag#</span>
  </loop>
</div>
<!-- Pass JSON array -->
<what-tag-list tags='["rust", "html", "css"]'></what-tag-list>

<!-- Or pass fetched data -->
<what-tag-list tags="#post.tags#"></what-tag-list>

Composing Components

Use one component inside another to build complex UIs from simple parts:

components/post-card.html
<what>
props = "title, author, tags"
defaults.tags = "[]"
</what>

<what-card title="#title#">
  <p class="text-muted">By #author#</p>
  <what-tag-list tags="#tags#"></what-tag-list>
  <slot/>
</what-card>
Avoid circular nesting. If component A uses B, and B uses A, you'll get an infinite loop.

Built-in Components

What ships with built-in components that you don't need to create yourself:

what-fetch

A region that fills itself from a partial — on render, on scroll into view, or on a timer. See Lazy Loading & Polling:

<what-fetch url="/w-partial/stats" poll="5s">
  <include src="partials/stats"/>
</what-fetch>

what-clipboard

A copy-to-clipboard button. See Zero JavaScript:

<what-clipboard value="cargo install run-what" copied-label="Copied!">Copy</what-clipboard>

what-theme-toggle

A dark/light theme toggle, persisted across visits with no flash-of-wrong-theme:

<what-theme-toggle/>

what-pagination

Generates pagination controls for loops with the paginate attribute:

<loop data="#posts#" as="post" paginate="10">
  <div>#post.title#</div>
</loop>

<what-pagination base_url="/blog" current="#page#" total="#total_pages#">
</what-pagination>

what-turnstile

Adds Cloudflare Turnstile CAPTCHA protection to forms:

<form w-action="create" w-store="contacts">
  <input name="email" type="email" w-required>
  <what-turnstile sitekey="your-site-key"></what-turnstile>
  <button type="submit">Submit</button>
</form>