# At a Glance One page of the whole framework: a route, a template, an rpc, a job, a LiveTable, a test. A small real-time comment app. Most of Elements in one place: html templates, a route, a migration, and a `LiveTable`. **index.ts** ```typescript import { App } from "@elements/app"; import config from "#config"; import home from "#app/pages/home"; const app = new App(); app.route("/", home); app.start(config); ``` **app/pages/home/models.ts** ```typescript export interface Comment { id: string; text: string; selected?: boolean; } ``` **app/migrations/20260509120000-add-comments-table.migration.sql** Generated by `elements create migration "add comments table" -tables=comments`. The `-tables` flag scaffolds the canonical baseline (`id`, `createdAt`, `updatedAt`, and the `touchUpdatedAt` trigger) for each named table. Pass `-tables=comments,users` to scaffold several at once. ```sql create or replace function touchUpdatedAt() returns trigger language plpgsql as $$ begin new.updatedAt = now(); return new; end; $$; create table comments ( id uuid primary key default uuidGenerateV7(), text text not null, createdAt timestamptz not null default now(), updatedAt timestamptz not null default now() ); create trigger commentsTouchUpdatedAt before update on comments for each row execute function touchUpdatedAt(); ``` `id` is UUIDv7: time-sortable, so newer rows sort after older ones without a separate timestamp index. Columns are camelCase in the migration. Elements converts to snake_case at the database boundary and back to camelCase on results. Save the file and the project server applies the migration to the development database. Edit the file (add a column, an index, a constraint) and the project server rolls back and re-applies. Once a migration has run on staging or production it's frozen and never runs again. Agents don't run `elements db migrate` or touch the database directly. **app/pages/home/index.ts** ```typescript import { Request, Response, LiveTable } from "@elements/app"; import { Comment } from "./models"; import html from "./template"; // LiveTable for the `comments` table, inferred from the variable name. // Wires up select/insert/update/delete rpc and optimistic browser-side // mutations. Realtime is on by default, so every watching browser sees // new rows live over Postgres NOTIFY/LISTEN. Pass `realtime: false` for // a snapshot read. const comments = new LiveTable(); export default function route(req: Request, res: Response) { return new html({ comments, mode: req.params.mode ?? "light", }); } ``` **app/pages/home/template.html** ```html import { Layout } from "#app/shared/templates/layout"; import { Comment } from "./models"; import { LiveTable } from "@elements/app"; import "./style.css"; // event handlers function onAddComment(form: { text: string }, comments: LiveTable) { comments.insert({ text: form.text }, () => form.text = ""); } // templates , mode: string = "dark") class="home">
  • comment.selected = true} onblur={() => comment.selected = false}> {comment.text}
  • , private form: { text: string } = { text: "" })>
    onAddComment(form, comments)}>
    ``` This one example covers: `App` and `app.route()`, a migration with `id`/camelCase/trigger, a page route handler returning `new html({...})`, a `LiveTable` with select/insert/update/delete and realtime, template attributes with `private` and defaults, sub-templates and ``, `e:for` (keyed by `id`), event handlers, two-way form binding with `value=`, reactive class arrays, and lifecycle-style reactivity via mutating `comment.selected`.