Manual Getting Started Project Layout

Project Layout

elements man start/layout Read as markdown

What 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.