GETTING STARTED

Quick start

One command asks three questions — static, server-rendered or browser app; with or without the sample island; TypeScript or not — and leaves a working project behind. Node 22 or newer.

Create a project

terminal
$ npm create amojs my-app
$ cd my-app
$ npm install
$ npm run dev

Outside a terminal (CI, scripts) nothing is asked and the defaults apply: static site, with the island, JavaScript. Flags skip the questions:

terminal
$ npm create amojs my-app -- --ssg --no-island --ts

--ssg      static site: pages render to .html at build time (the default)
--ssr      server-rendered: amo serve ssr renders pages per request
--csr      browser app: everything renders on the client
--island / --no-island   with or without the sample counter island (ssg/ssr only)
--ts / --js              language (default JavaScript)

What you get

No infrastructure files — the CLI owns the pipeline. Pages run on node and render to HTML; islands are the only JavaScript a visitor downloads.

my-app/
my-app/
  package.json          dev · build · serve (· check for ts)
  src/
    pages/index.js      rendered on node — ships zero JS of its own
    lib/head.js         shared by every page (imported as ../lib/head.js)
    islands/counter.js  the interactive piece — the only client script
    styles/site.css
    public/favicon.svg  files served from the site root
  .vscode/              recommends the AmoJS editor extension

The three scripts

terminal
$ npm run dev       # build + watch + serve on :4700 — css hot-swaps, islands re-mount, pages reload
$ npm run build     # ssg: static .html · ssr: server modules · csr: browser modules → dist/
$ npm run serve     # run what build made on :3000 (for ssr, this IS the server)

Each is amo <verb> <mode> and nothing more. The grammar is symmetric: dev, build and serve, each in csr, ssg or ssr.

Or skip the toolchain entirely

AmoJS is plain ESM. For an experiment, an HTML file is enough — no package, no build, no server:

index.html
<script type="module">
  import { signal, html, mount } from 'https://esm.sh/@amojs.dev/core';

  const count = signal(0);
  mount(() => html`<button onclick=${() => count.value++}>${count}</button>`, document.body);
</script>

The compiler is an optimizer you add when you ship. Everything on this site works the same way in both modes, by test.

Where next