# HTML Elements HTML is a reactive HTML language extension and runtime. It is standard HTML, augmented with lightweight extensions for creating templates, declaring template constructors, and embedding reactive expression blocks. The language adds a few new attributes (`e:if`, `e:switch`, `e:for`, and `group`) and automatic two-way binding on input elements. A reactive runtime attaches to the server-rendered page and updates the DOM surgically as data changes. Elements HTML files end in `.ehtml`; a `.html` file is plain HTML. Templates are the unit of composition. They scale from one to many: define them in a single file or across multiple files and import them like any other TypeScript export. Each template has a template constructor, a new syntax for declaring typed attributes on the opening tag (``). Template constructors are an extension to TypeScript. Attributes are fully typed and reactive, and the codebase's refactoring tools (rename, find-references, go-to-definition) work across templates the same way they work across TypeScript code. A page is a template named `` exported by default from an `.ehtml` file. Reactivity is simple: if the data changes, any UI that depends on that data updates automatically. The runtime patches the DOM at the smallest level required by every change. Reactivity crosses template boundaries: a child mutating an attribute received from a parent updates the parent without any plumbing. Form input bindings are two-way by default. `value`, `checked`, and `group` keep template state and DOM state synchronized without any extra event wiring. Editing an input updates the bound variable; updating the bound variable updates the input. Elements HTML renders on the server and the browser by design. The initial response is the fully-rendered DOM, so search engines and first-time visitors get the complete page immediately, with no client-side bundle to download before the first render. After the initial render, the browser attaches and adds reactivity and event handling. A route can also return live data alongside the page, like channel listeners and LiveTables, and the browser keeps consuming those updates continuously after attach. The additions are intentionally narrow: a TypeScript expression syntax (`{...}` anywhere HTML accepts a value), attributed top-level tags as reusable templates (`...`), a small set of control-flow directives prefixed `e:` (`e:for`, `e:if`, `e:switch`), and TypeScript colocated with the template (imports, helpers, route handlers, `@rpc` functions, all in the same file). That is the entire syntactic surface. Everything else lives in the runtime and the compiler. The compiler is built on top of the TypeScript tooling. Templates participate in the same build graph as the rest of your code: same type checker, same go-to-definition, same find-references, same LSP. An attribute typed `User` is checked everywhere it flows. Type errors in template source surface at build time. 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. ## At a Glance ```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.