AvalonAvalon
GitHub

Quick Start

Build your first Avalon page with an interactive island in under 5 minutes.

Scaffold a new project

The fastest way to get started is with the Avalon CLI:

npx avalon my-app

For Bun users:

bunx avalon my-app

This will walk you through selecting your frameworks, styling approach, and deployment target.

Install dependencies

cd my-app
npm install

Or with Bun:

cd my-app
bun install

Start the dev server

npm run dev

Open http://localhost:3000 in your browser. You should see the Avalon welcome page.

Create a page

Pages live in src/pages/. Create src/pages/hello.tsx:

export default function HelloPage() {
  return (
    <div>
      <h1>Hello, Avalon</h1>
      <p>This page ships zero JavaScript.</p>
    </div>
  );
}

Visit http://localhost:3000/hello — you'll see your page rendered as pure HTML with no client-side JavaScript.

Add an island

Islands are interactive components that hydrate on the client. Create src/islands/Counter.tsx:

/** @jsxImportSource preact */
import { useState } from 'preact/hooks';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}

Then use it in your page with the island prop to control when it hydrates:

import Counter from '../islands/Counter.tsx';

export default function HelloPage() {
  return (
    <div>
      <h1>Hello, Avalon</h1>
      {/* Hydrates only when the user first interacts with it */}
      <Counter island={{ condition: 'on:interaction' }} />
    </div>
  );
}

Build for production

npm run build

Avalon builds a Nitro server bundle in .output/. Deploy it to Node, Bun, Cloudflare Workers, Vercel, or any Nitro-compatible target.

Try it live

Here's a live example of an island component — it starts in a static state and becomes interactive on your first interaction: