# Elements HTML Elements HTML is the HTML you and your agent already know, with a lightweight syntax extension: TypeScript expressions in attributes and in the body, and templates that take typed attributes the way a function takes parameters, public or private. It is reactive and dynamic: change the data and the page updates. It runs on the server and in the browser automatically, from one file, with nothing to configure. The runtime is extremely fast, and does not rely on virtual DOM diffing. This is not JSX: not a custom language rendered through a tree diffing algorithm, with the performance problems that brings, and which in many cases does not render on the server by default. Agents write it flawlessly, because it is standard HTML. Converting to and from Elements HTML is easy. ```ehtml A page

{title}

``` That is a complete page. The route passes `title`, the template owns `count`, the expression in the body prints it, and the click updates it in place. ## Templates A template is a top-level tag whose name starts with a capital letter. It can take a constructor: attributes declared in `()` inside the opening tag, before its `>` and never in the body (see [attributes](attributes)), typed like function parameters, each one `public` (the default) or `private`. A template with no attributes needs no constructor. A default makes an attribute optional, and an initializer can read other attributes. Callers set public attributes the way they set any HTML attribute. A `private` attribute is state the template owns: callers cannot set it, and the template can change it. ```ehtml Attributes

open = !open}>{greeting}

``` A page is a template named ``, the default export of its `.ehtml` file. Any other top-level tag is a named export, imported like any TypeScript export, so templates scale from one file to many. Attributes are fully typed and the codebase's tooling (rename, find-references, go-to-definition) works across templates the same way it works across TypeScript. ## Reactivity If the data changes, any UI that depends on it updates. The runtime knows which data each binding reads and patches that one text node, attribute or element, with no virtual DOM to rebuild and compare. A list is keyed, so a row that moved is moved, not re-rendered. Reactivity crosses template boundaries: a child mutating an attribute it received from a parent updates the parent, with no plumbing. ```ehtml Lists, conditions and classes
  • {user.name}
  • no users yet

    {users.length} users

    ...
    ``` A class array drops its falsy entries, so `is-active` comes and goes with `active`, and nothing else on the element is touched. The page's attributes are live in the browser console as `$`. Assign to one and the page updates through the same path the template uses, so you can jump to any state without clicking through the UI or rebuilding ([reactivity](reactivity)). ```js Browser console $.title = "new title"; // re-renders the title $.users = []; // shows "no users yet" ``` ## Form bindings Input bindings are two-way by default. `value`, `checked` and `group` keep the bound variable and the control in step with no event wiring: editing the input updates the variable, and updating the variable updates the input. ```ehtml Bindings ``` ## Server and browser Every page renders on the server, so the first response is the complete HTML: search engines and first-time visitors get the finished page with no bundle to download before the first paint. The runtime then attaches to that HTML in the browser, and from there the page is reactive: event handlers run in the browser, bindings are live, and changes patch the DOM in place. The server never renders an update and never sends HTML after the first response. When the page asks the server for something (an `@rpc` call, a channel message, a LiveTable row), the reply is data, and the browser renders it. The Elements compiler type checks an `.ehtml` file with the rest of your TypeScript: same type checker, same build graph, same LSP. An attribute typed `User` is checked everywhere it flows, and a typo in a template is a build error, not a blank page. The compiler emits the JavaScript for the server and the browser and ships the runtime alongside. Elements HTML files end in `.ehtml`; a `.html` file is plain HTML. ## At a Glance ```ehtml template.ehtml import { Layout } from "#app/shared/templates/layout"; import "./style.css"; interface Form { name: string; age: number; } /** @rpc */ function saveUser(form: Form): User { return sql(` insert into users ( name, age ) values ( ${form.name}, ${form.age} ) returning * `).firstOrThrow("insert returned no row"); } function onSubmit(form: Form, resetUI: () => void) { resetUI(); saveUser(form); }

    {title}

    • {user.name} ({user.age})
    onSubmit(form, () => form = { name: "", age: 0 })}>

    no users yet

    {users.length} users

    ``` The page renders fully on the server and attaches in the browser. The form is two-way bound, submit calls an `@rpc` function, and the list updates reactively whenever `users` changes. ## The file and the page tag An `.ehtml` file is a mix of templates and TypeScript code. A template is a top-level tag whose name starts with a capital letter, which is what distinguishes it from a native HTML element and is required rather than a convention. The `` tag is the one exception: it is reserved for the page and is the default export, automatically bundling its code and stylesheets across the reachable dependency graph. Any other top-level tag is a named export. TypeScript code goes anywhere outside a template declaration, like imports, helpers, `@rpc` functions, and event handlers. Because the page tag is a real ``, attributes written on it land on the document element. `` puts `page-chat` on ``, so use it to scope custom properties and theming and put layout on an element inside the page. `elements create page` writes that `page-` prefix so the page class stays out of the namespace component classes live in: a bare `.chat` rule written for a widget somewhere else in the app would match `` and restyle the whole document. A page gets type, colour and spacing by default and no width at all; see [base](../style/base) and add `.page-shell` to a `
    ` when you want a measured, centred column.