Configuration
Introduction
ReeWeb has very little to configure. Everything that varies between environments lives in .env and is read directly from Bun.env. Everything that varies between projects lives in TypeScript files under config/ that you edit like any other source file.
The .env File
Copy the example file:
cp .env.example .env
The full set of variables:
SITE_URL=https://example.com
BASE_URL=/
PORT=3000
BLOG_DIR=engineering-notes
OPEN_IDE=vscode
| Variable | Required | Purpose |
|---|---|---|
SITE_URL | Yes | Full site URL, used for canonical/hreflang links and the feeds/sitemap |
BASE_URL | Yes | Base URL path the site is served from (/ for a domain root) |
PORT | Yes | Local server port for the dev server and preview server |
BLOG_DIR | Yes | Sub-directory under the public dir that holds blog posts, for the RSS/JSON feeds |
OPEN_IDE | No | Which editor the dev inspector opens |
The required variables are strict and env-only: each is read from .env (no fallback to package.json or a hidden code default), and the script exits with an error if the value is missing. Where a script accepts an equivalent CLI flag (--site-url, --base-url, --port, --blog-dir), the flag takes precedence over the environment value. This keeps every configurable default visible in one place - your .env - rather than buried in code.
SITE_URL- when set, the SSG generates<link rel="alternate" hreflang="...">tags pointing to absolute URLs for each language variant, required by Google for multi-language SEO. Read by thessg,sitemap, andrssscripts.BASE_URL- the URL path the site is mounted at;/for a domain root, or e.g./docs/when served from a sub-path.PORT- the port the dev server (bun dev) and preview server (bun preview) listen on.BLOG_DIR- the folder undersrc/public/scanned for blog posts when generating the RSS and JSON feeds.
OPEN_IDE is the one optional variable: it selects which editor the dev inspector's "open in editor" action launches - one of vscode, zed, nvim, sublime, or idea. It is not required to run the dev server or generate the site; it is only consulted when you use that inspector action, which reports a clear error if OPEN_IDE is unset. See Dev Inspector - Editor Configuration.
SSG Options
The bun ssg script accepts several flags:
bun scripts/ssg.ts --public ./src/public --dist ./dist --base-url / --site-url https://example.com --verbose
| Option | Default / Source | Description |
|---|---|---|
--public | ./src/public | Source directory with .ree templates |
--dist | ./dist | Output directory for static HTML |
--base-url | BASE_URL (env) | Base URL for the site |
--site-url | SITE_URL (env) | Full site URL for canonical/hreflang |
--verbose | false | Log each rendered file |
--base-url and --site-url have no code default: when the flag is omitted they read BASE_URL / SITE_URL from .env, and the script exits with an error if neither the flag nor the environment variable is set.
config/supported_languages.ts
Declares the languages your site supports:
export const languages = ["en", "sl"] as const;
export const active_languages = ["sl", "en"] as const;
export const soft_launch_languages: string[] = [];
export const default_language = "sl";
export const language_names: Record<string, string> = {
en: "English",
sl: "Slovenian",
};
export const language_locales: Record<string, string> = {
en: "en-US",
sl: "sl-SI",
};
| Export | Purpose |
|---|---|
languages | All languages with translation files |
active_languages | Languages shown in the language chooser |
soft_launch_languages | Languages that are generated but excluded from sitemaps, feeds, hreflang links, and the language chooser until ready |
default_language | Language served at the site root (no prefix) |
Adding a new language is a one-file change here plus a matching {lang}.json translation file.
config/redirects.ts
Declares URL redirects (301 by default, 302 for temporary):
export const redirects: { from: string; to: string; status?: 301 | 302 }[] = [
{ from: "/old-page", to: "/new-page" }, // 301
{ from: "/promo", to: "https://example.com/landing", status: 302 }, // 302
];
Redirects are validated in two phases by lib/redirects.ts:
- Schema validation - checks
fromstarts with/, has no file extension,tois non-empty - Collision check - after rendering, verifies
fromdoesn't collide with a page or asset
Emitted as:
dist/_redirects- Cloudflare Pages formatdist/{from}/index.html- HTML meta-refresh stubs (fallback for other hosts)
config/pagination.ts
Declares which routes get paginated output and how. The default config enables pagination for the blog:
export const pagination: PaginationConfig = {
enabled: true,
per_page: 10,
path_segment: "", // /blog/2/ - no untranslated "page" in the URL
show_when_single_page: false,
always_show_prev_next: true,
variant: "full", // "full" or "simple"
routes: [
{ route: "blog" },
],
};
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Master switch. false disables pagination entirely. |
per_page | number | 10 | Default items per page (routes can override). |
path_segment | string | "" | URL segment before the page number. "" gives /blog/2/; "page" gives /blog/page/2/. |
show_when_single_page | boolean | false | Show pagination element when all results fit on one page. |
always_show_prev_next | boolean | true | Always render disabled Previous/Next buttons. |
variant | "full" | "simple" | "full" | Which pagination component variant is used. |
routes | PaginationRoute[] | [] | Routes to paginate. Each can override per_page, set source ("markdown" or "loader"), and sort order. |
Records are collected from markdown files by default. To supply records from an external source, export load_records(lang) from the route's index.ts. See Pagination.
package.json Scripts
The scripts you'll use most:
| Script | Purpose |
|---|---|
bun dev | Development server + Tailwind CSS watcher (concurrent) |
bun ssg | Generate the static site to dist/ |
bun preview | Serve dist/ locally |
bun css:build | Build minified Tailwind CSS from src/css/style.css |
bun css:watch | Watch and rebuild CSS during development |
bun format | Format all source files (reettier) |
bun sitemap | Generate sitemap.xml |
bun rss | Generate RSS feed |
For hreflang links, pass --site-url directly:
bun scripts/ssg.ts --public ./src/public --dist ./dist --site-url https://example.com --verbose
How Bun Loads .env
Bun reads .env files automatically at startup - there's no dotenv dependency to install. Variables become available on Bun.env.NAME.