Installation

ReeWeb has one hard prerequisite - Bun. The CLI is the runtime, the package manager, the bundler, and the test runner all in one. bun create fetches the public starter repository, and the install task then fetches the rest of the tooling it needs (Git is only required if you prefer to clone the starter yourself).

This page is the first-time setup. Once you've worked through it, Project Structure walks you through what the starter gives you.

Installing Bun

Use the official installer:

macOS / Linux / WSL
curl -fsSL https://bun.sh/install | bash
Windows
powershell -c "irm bun.sh/install.ps1|iex"

Verify the install:

bun --version

Upgrade to the latest stable release:

bun upgrade

The output should be the latest stable Bun version. If bun: command not found, the installer's ~/.bun/bin directory isn't on your $PATH - open a new terminal or run source ~/.bashrc (or ~/.zshrc) to pick up the change.

Pinning a Specific Version

For a project that needs a specific Bun version (matching a teammate's environment, matching production), install that version explicitly by appending the version tag (<version>):

macOS / Linux / WSL
curl -fsSL https://bun.sh/install | bash -s "bun-v<version>"
Windows
iex "& {$(irm https://bun.com/install.ps1)} -Version <version>"

Then leave bun upgrade alone until you've tested a newer version against your project. ReeWeb works with the latest stable Bun; a recent version is safe.

Creating Your First Project

Follow these steps to get a ReeWeb project running on your machine.

1. Create a Project

Pick a name for your project folder. Set it once and every command on this page updates to match, so you can copy-paste them as-is:

bun create reepolee/reeweb __PROJECT_DIR__
cd __PROJECT_DIR__

bun create fetches the starter as a tarball, so the template's Git history never arrives - the installer initializes a fresh repository with a single initial commit instead.

Prefer a plain source checkout instead? Clone the public repository and run bun install - the first dependency install fires the same install script, and the installer still replaces the cloned history with your project's own initial commit:

git clone https://github.com/reepolee/reeweb.git __PROJECT_DIR__
cd __PROJECT_DIR__
bun install

2. Everything Installs Automatically

With bun create reepolee/reeweb, the install script runs by itself as part of project setup - the fetch installs dependencies and the postinstall step fires the installer, so there is no separate command to run. After a git clone, your first bun install triggers the same step. It installs:

  • Project dependencies - installed by the same bun install that fires the installer
  • Tailwind CSS v4 - unlike Reepolee's standalone binary, ReeWeb pulls in Tailwind as an ordinary dev dependency, alongside tailwind-merge
  • TypeScript & type definitions - Bun's own types (@types/bun) and @cloudflare/workers-types for the Workers runtime. None of these ship to the browser: ReeWeb has zero runtime dependencies
  • reettier - the Ree template formatter
  • reesql - the SQL formatter
  • Vendored browser packages - the vendor/ assets referenced by the starter's templates
  • libvips - VIPS CLI, for the Open Graph card pipeline (see libvips below)
  • .env - copied from .env.example when it doesn't already exist

It does not build CSS or prepare images; bun dev and bun ssg do that when they run.

If you ever need to re-run the setup manually, the script is bun reeweb:install. See ReeWeb & Reepolee if you're weighing the two frameworks.

Next, open .env and set SITE_URL to your real domain. .env.example ships SITE_URL=https://example.com, and that value is baked into sitemap.xml, robots.txt, the feeds, and every canonical and hreflang link the first time you build.

3. Verify the Setup

bun dev

If the dev server starts and the page loads in a browser, your setup is complete.

Generating the Static Site

bun ssg

bun ssg is the whole production build, not just the HTML render. It runs, in order:

  1. dynamic:sync, which pulls dynamic assets when REEPOLEE_API_URL is set
  2. prepare:images, which generates responsive images from assets/images/
  3. css:build, which compiles and minifies Tailwind CSS
  4. scripts/ssg.ts, the static site generator itself
  5. og:images, the Open Graph card for every page (see libvips)
  6. sitemap, then rss, then ssg:search

The generator step (scripts/ssg.ts) is the one that:

  • Renders all .ree templates and .md files to static HTML
  • Copies static assets (CSS, JS, images, fonts) to the output directory
  • Generates localized routes for every configured locale
  • Emits redirects for paths declared in config/redirects.ts

Because bun ssg already covers the sitemap, feeds, and search index, you do not need to run those separately after it. The individual scripts in Other Commands exist for re-running a single stage.

The output goes to dist/. Serve it with any static file server:

bun preview

This starts a lightweight HTTP server on the PORT from your .env (3000 by default) serving the generated files. Stop bun dev first: both servers bind the same port, and unlike bun dev, bun preview does not reclaim it - it exits with EADDRINUSE instead.

Development Mode

bun dev runs scripts/dev/orchestrate.ts, which performs the one-shot setup steps (asset sync, image preparation, initial CSS build), then runs the Tailwind CSS watcher (--watch=always) and the development server (scripts/dev.ts) side by side - bun dev is a single command. Changes to templates, markdown, translations, or CSS are detected automatically and the browser reloads; no manual refresh needed.

The CSS watcher recompiles src/css/style.css to src/public/css/style.min.css on every change.

Other Commands

PurposeCommand
SSG with verbose loggingbun scripts/ssg.ts --public ./src/public --dist ./dist --verbose
SSG with hreflang linksbun scripts/ssg.ts --public ./src/public --dist ./dist --site-url https://example.com
CSS once (minified)bun css:build
CSS watchbun css:watch
Generate sitemapbun sitemap
Generate RSS feedbun rss
Generate search indexbun ssg:search
Generate Open Graph cardsbun og:images
Format sourcebun format (uses reettier)
Re-fetch tooling and vendor assetsbun get:pre

Verifying the Production Build

Stop bun dev if it is still running, then:

cd __PROJECT_DIR__
bun ssg
bun preview

If you see "✅ Static Site Generation complete" in the terminal and the preview loads at http://localhost:3000, everything is wired up correctly.

Editor Setup

For VSCode, the Ree Templates extension adds syntax highlighting and formatting for .ree files. Install it from the marketplace or with the command-line:

code --install-extension reepolee.ree-templates

For other editors:

  • Treat .ree as HTML. The HTML syntax highlighter handles most of Ree fine - the tag prefixes ({= , {#if}, {#each}) are visually distinct enough that the HTML grammar ignores them cleanly.
  • Tailwind IntelliSense (the official VSCode extension) works inside .ree files if you tell it to - add "tailwindCSS.includeLanguages": { "ree": "html" } to your VSCode settings.

The TypeScript language server picks up .ts files in ReeWeb projects natively - no extra configuration needed.

For the full LSP feature set — autocompletion, go-to-definition, hover, and translation-key support — see Editor & LSP.

A note about libvips for Open Graph images

bun ssg generates a PNG Open Graph card for every page as part of its pipeline (the og:images step, which runs after the generator finishes and before the sitemap and feeds), and that step shells out to the native libvips library. Reepolee ships the same installer for its own avatar and image-editor pipeline, so the two frameworks share one setup story.

bun reeweb:install already installs it for you, by way of bun get:pre, so a project set up as described above needs nothing extra. To install or repair it on its own:

bun get:vips

This supports Windows (prebuilt from GitHub releases), macOS (Homebrew), and Linux (apt/dnf/pacman), and adds vips to your PATH automatically. Prefer bun get:vips over calling bun scripts/cli.ts vips directly - the script pins the version ReeWeb is tested against, and the Windows installer needs a real version number to pick a release asset. Without libvips, bun ssg fails on the og:images step; bun dev does not need it, since it doesn't generate Open Graph images.

Every rendered page needs a non-empty <title>. The og:images step reads it to build that page's card and fails the entire bun ssg run - not just that one card - if a page has no <title> tag, or an empty one (<title></title>). This is intentional: a page with no title is incomplete regardless of Open Graph, so the build treats it as a hard error rather than silently shipping a titleless page. layout.ree already sets one from props.title / props.site_name; a template that renders without going through a layout is the case to watch.

What's Next?

After the install task completes, your project is ready. Project Structure walks you through the directory layout, and Configuration covers the ree section in package.json.

If anything fails during setup - missing Bun, broken bun install, the dev server not starting - fix it now before moving on. The rest of the documentation assumes the install works.