RxHTML Fundamentals

RxHTML compiles to JavaScript and runs in the browser. But unlike normal HTML where you have a single document, RxHTML uses a forest structure — multiple pages and reusable templates all living under one roof.

The Forest Structure

In RxHTML, the root element isn't <html>. Instead, you create a <forest> that holds your pages and templates:

<forest>
  <page uri="/">
    <h1>Welcome</h1>
  </page>

  <page uri="/about">
    <h1>About Us</h1>
  </page>

  <template name="header">
    <nav>Navigation here</nav>
  </template>
</forest>

A forest can span multiple .rx.html files. The compiler merges them all into a single application, which is nice because you can organize things however makes sense to you.

Pages

Pages are full-screen documents routable via URI. Each one declares what URL path activates it:

<forest>
  <page uri="/">
    <h1>Home Page</h1>
  </page>

  <page uri="/products">
    <h1>Our Products</h1>
  </page>

  <page uri="/contact">
    <h1>Contact Us</h1>
  </page>
</forest>

URI Parameters

URIs support dynamic parameters using the $ prefix:

<forest>
  <page uri="/product/$id">
    <h1>Product Details</h1>
    <p>Viewing product: <lookup path="view:id" /></p>
  </page>

  <page uri="/user/$username/posts/$post_id">
    <h1>Blog Post</h1>
  </page>
</forest>

Parameters get extracted from the URL and dropped into the view state. Access them with the view: prefix:

URL View State
/product/42 view:id = "42"
/user/alice/posts/7 view:username = "alice", view:post_id = "7"

Authentication Requirements

Pages can require authentication:

<forest>
  <page uri="/dashboard" authenticate>
    <!-- Only accessible when logged in -->
    <h1>Your Dashboard</h1>
  </page>

  <page uri="/login" default-redirect-source>
    <!-- Unauthenticated users end up here -->
    <h1>Please Log In</h1>
  </page>
</forest>

The authenticate attribute marks a page as requiring auth. If someone shows up without being logged in, they get redirected to whatever page has default-redirect-source. Simple enough.

Templates

Templates are reusable UI components — shared HTML you can invoke from pages or other templates:

<forest>
  <template name="site-header">
    <header>
      <h1>My Application</h1>
      <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
      </nav>
    </header>
  </template>

  <page uri="/">
    <div rx:template="site-header"></div>
    <main>
      <p>Welcome to the home page!</p>
    </main>
  </page>

  <page uri="/about">
    <div rx:template="site-header"></div>
    <main>
      <p>About our company...</p>
    </main>
  </page>
</forest>

Template Invocation

Invoke a template with the rx:template attribute:

<div rx:template="my-template">
  <!-- This content becomes available via <fragment /> -->
</div>

The element carrying rx:template becomes a container. Its children get passed to the template and are accessible via the <fragment> element.

The Fragment Element

Templates can inject the caller's children using <fragment>:

<forest>
  <template name="card">
    <div class="card">
      <div class="card-header">
        <fragment case="title" />
      </div>
      <div class="card-body">
        <fragment />
      </div>
    </div>
  </template>

  <page uri="/">
    <div rx:template="card">
      <span rx:case="title">Welcome</span>
      <p>This is the card body content.</p>
    </div>
  </page>
</forest>

The case attribute on <fragment> and rx:case on children give you named slots. So you can pass different chunks of content to different parts of a template. It's essentially slot-based composition without the ceremony.

Inline Templates

If you don't want a wrapper element cluttering up your DOM, use <inline-template>:

<page uri="/">
  <inline-template name="site-header">
    <!-- Children merged directly into parent -->
  </inline-template>
  <main>Content</main>
</page>

The Two Reactive Trees

This is the part that matters most. RxHTML maintains two separate data sources:

View State (Client)

The view state belongs to the client. It holds:

  • UI state (selected tabs, expanded sections, filters)
  • Form values before submission
  • URI parameters
  • Temporary client-side data

Access it with the view: prefix:

<lookup path="view:search_query" />

View state gets sent to the Adama document asynchronously. Your Adama code reads it via @viewer:

record Item {
  public string title;
}
table<Item> _items;
view string filter;

// In Adama
bubble filtered_items = iterate _items
  where @viewer.filter == "" || title.contains(@viewer.filter);

Data State (Server)

The data state comes from the Adama document over the WebSocket. It holds:

  • Document fields
  • Table data
  • Bubble results computed for the current viewer

Access it directly (no prefix), or explicitly with data::

<lookup path="title" />
<lookup path="data:title" />  <!-- equivalent -->

Path Navigation

Paths navigate the hierarchical data structure:

Syntax Meaning
field Access field in current scope
/field Navigate to document root, then access field
../field Navigate to parent, then access field
child/field Navigate into child object, then access field
view:path Access view state instead of data state
data:path Explicitly access data state

The Shell

A forest can have one <shell> element that configures the generated application:

<forest>
  <shell>
    <meta charset="UTF-8">
    <title>My Application</title>
    <link rel="stylesheet" href="/styles.css">
    <script src="/custom.js"></script>
  </shell>

  <page uri="/">
    <!-- Page content -->
  </page>
</forest>

The shell defines your HTML head content — stylesheets, scripts, meta tags, and the default title.

File Organization

RxHTML applications typically organize files by concern:

frontend/
  shell.rx.html      # Shell and global configuration
  templates.rx.html  # Shared templates
  pages/
    home.rx.html     # Home page
    products.rx.html # Product pages
    auth.rx.html     # Login/signup pages

All .rx.html files in your frontend directory get merged into a single forest. Organize however you like; the compiler doesn't care about your folder structure.

Now go learn how to connect to Adama and bind data.

Previous Rxhtml