Project Layout
elements man start/layout Read as markdownWhat elements create writes, what each directory is for, and where your
code goes.
The first file you likely want to edit is app/pages/home/template.html, the
page the user has open in their browser.
.elements/ # private build/server state; do not read, write, or search here
AGENTS.md # agent instructions (this getting-started guide)
index.ts # app entry: creates the App, registers routes, starts the server
worker.ts # services setup for job and test worker processes
config.jsoc # dependencies, the import map, and app config
package.lock # resolved dependency lockfile (generated; don't edit by hand)
config/
env/
development.env # committed; dev-safe values
production.env # gitignored; production secrets
app/
pages/
home/
index.ts # route handler
template.html # page markup and inline TypeScript
style.css # page styles
test.ts # page tests
services.ts # per-page rpc + shared interfaces (add by hand as the page grows)
errors/
not-found/ # 404 page
unhandled/ # 500 page
shared/
templates/ # reusable templates (layout, etc.)
services/ # cross-page rpc, LiveTables, channels
styles/
page.css # site-wide page baseline (imports @elements/style)
email.css # site-wide email baseline
vars.css # design-token overrides
assets/ # favicons, images
jobs/ # background jobs
emails/ # email templates
migrations/ # generated migration files
types/
session.d.ts # SessionData augmentation (types session.get())
node_modules/
There is no root package.json or tsconfig.json. Dependencies, the import
map, and TypeScript options all live in config.jsoc, and the resolved
dependency set is written to package.lock (don't edit it by hand). Some
folders (app/jobs/, app/emails/, app/shared/services/,
app/migrations/) are created as you need them.
Hidden folders at the project root (anything starting with ., like
.elements/, .git/, .claude/) are not part of your source tree. Don't
grep, glob, or otherwise search inside them. Ripgrep and editor search already
skip hidden folders by default; if you shell out to grep -r or find, pass
--exclude-dir=.elements (grep) or -not -path '*/.elements/*' (find).
The Entry Point
index.ts at the project root constructs the App, registers routes, and
starts the server:
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);
worker.ts brings up the shared services (the database pool, email) that job
and test worker processes need, so sql(), tx(), and email() work inside
jobs and tests the same way they do in an @rpc handler. Delete it if your app
has no jobs and no database-touching tests.