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:
curl -fsSL https://bun.sh/install | bash
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>):
curl -fsSL https://bun.sh/install | bash -s "bun-v<version>"
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 installthat 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-typesfor 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.examplewhen 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:
dynamic:sync, which pulls dynamic assets whenREEPOLEE_API_URLis setprepare:images, which generates responsive images fromassets/images/css:build, which compiles and minifies Tailwind CSSscripts/ssg.ts, the static site generator itselfog:images, the Open Graph card for every page (see libvips)sitemap, thenrss, thenssg:search
The generator step (scripts/ssg.ts) is the one that:
- Renders all
.reetemplates and.mdfiles 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
| Purpose | Command |
|---|---|
| SSG with verbose logging | bun scripts/ssg.ts --public ./src/public --dist ./dist --verbose |
| SSG with hreflang links | bun scripts/ssg.ts --public ./src/public --dist ./dist --site-url https://example.com |
| CSS once (minified) | bun css:build |
| CSS watch | bun css:watch |
| Generate sitemap | bun sitemap |
| Generate RSS feed | bun rss |
| Generate search index | bun ssg:search |
| Generate Open Graph cards | bun og:images |
| Format source | bun format (uses reettier) |
| Re-fetch tooling and vendor assets | bun 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
.reeas 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
.reefiles 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.