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.ehtml, 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.ehtml     # 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). Quote a glob you pass to a flag, as in grep -rn load app --include='*.ts'. zsh, the default shell on macOS, fails the whole command on an unquoted glob that matches no file, and nothing after it on the line runs.

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.

Creating From a Scaffold

-scaffold creates the app from a published Elements app instead of the built-in templates:

elements create myshop -scaffold=elementscode/scaffold-ecommerce

Name the scaffold as a GitHub repository in any of its spellings (shorthand, the address bar, the clone url, the ssh remote) or as a link to a .tar.gz archive. Add @ref for a branch, a tag, or a commit.

Elements fetches the scaffold as an archive, so nothing here needs git installed and no history comes with it. The project is created first, exactly as it is without -scaffold, and the scaffold is extracted over it. AGENTS.md is rewritten with the current directives, and anything under .elements in the archive is skipped so the project keeps its own id.

A scaffold writes an app's own config.jsoc, styles, and entry point, so it applies to a new project and not over one that already has files in it. A half-applied scaffold is not a smaller app, it is a broken one. Creating the app and applying the scaffold is one step.

To publish a scaffold, push an Elements app to a public repository.