# 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})
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.