Project Structure
Introduction
ReeWeb follows a simple, opinionated directory layout where every folder has a clear, single purpose. Once you've worked on one ReeWeb project, you'll feel immediately at home in any other.
The Root Directory
A fresh ReeWeb project looks like this:
__PROJECT_DIR__/
├── config/ ← site configuration
│ ├── supported_languages.ts
│ ├── redirects.ts
│ └── pagination.ts
├── lib/ ← template engine, helpers, SSG utilities (framework - do not edit)
│ ├── template_engine.ts ← orchestrator
│ ├── template/ ← engine modules (compiler, custom_elements, includes, types)
│ ├── template_helpers.ts
│ ├── tw_merge.ts ← Tailwind class-merge helper
│ ├── collect_records.ts ← content collection & filtering
│ ├── content_visibility.ts ← draft/published visibility model
│ ├── hooks.ts ← project hook contract
│ ├── pagination.ts ← pagination view-model builder
│ ├── i18n.ts
│ ├── images.ts
│ ├── markdown_docs.ts
│ ├── static_site.ts
│ ├── route_aliases.ts
│ └── redirects.ts
├── scripts/ ← ssg and dev tooling (framework - do not edit)
│ ├── ssg.ts ← thin entry; phases live in scripts/ssg/
│ ├── ssg/ ← SSG modules (pipeline, render_*, routing, seo, …)
│ ├── dev.ts ← thin entry; modules live in scripts/dev/
│ ├── dev/ ← dev-server modules (render, resolve, watcher, …)
│ ├── preview.ts
│ ├── install.ts
│ ├── generate_sitemap.ts
│ ├── generate_rss.ts
│ └── prepare_images.ts
├── src/
│ ├── public/ ← templates, markdown, translations, static assets
│ │ ├── index.ree ← homepage
│ │ ├── index.ts ← homepage data loader
│ │ ├── layout.ree ← default layout
│ │ ├── en.json ← English translations
│ │ ├── sl.json ← Slovenian translations
│ │ ├── docs/ ← markdown docs section
│ │ ├── blog/ ← blog section
│ │ ├── about/ ← about page
│ │ ├── contact/ ← contact page
│ │ └── css/ ← page-specific CSS
│ ├── components/ ← reusable .ree components (source)
│ │ ├── banner.ree
│ │ ├── my-h1.ree
│ │ ├── full-pagination.ree
│ │ ├── simple-pagination.ree
│ │ ├── responsive-image.ree
│ │ └── speculation-rules.ree
│ ├── css/ ← Tailwind CSS source
│ │ └── style.css
│ └── lib/ ← project-specific code (safe to edit)
│ ├── project_hooks.ts ← your implementations of the framework hooks
│ ├── project_helpers.ts ← project-specific template helpers
│ └── markdown_styles.ts ← Tailwind classes for rendered markdown
├── static/ ← optional: compiled CSS, JS, images, fonts (served at /)
├── vendor/ ← vendored third-party libraries (e.g., highlight.min.js)
├── dist/ ← SSG output (generated)
└── package.json
The src/public/ Directory
The src/public/ directory is where your content lives. Every .ree file and .md file here becomes a page on your site. Files are organised by URL path:
src/public/index.ree→/src/public/about/index.ree→/about/src/public/blog/post-title/index.md→/blog/post-title/
Ordering prefixes (01_, 02_) on filenames are stripped when generating the canonical URL - docs/01_getting-started/02_installation.md becomes /docs/getting-started/installation.
Templates (.ree)
.ree files are your page templates. They use the Ree templating language with tags for output ({= }, {~ }), control flow ({#if}, {#each}, {#with}), layouts ({#layout()}), includes ({#include()}), and components (ReeTags). See ReeTags for the full reference.
Markdown (.md)
.md files are rendered as HTML by Bun's built-in markdown processor. They support:
- YAML frontmatter (title, layout, description, sidebar, etc.)
- Syntax highlighting via highlight.js (server-side, no client JS needed)
- Auto-generated heading IDs for table-of-contents links
- Tailwind CSS classes injected on headings, tables, code blocks, lists, blockquotes, and links
- External links automatically open in new tabs
Data Loading (.ts)
Templates can have sibling .ts files that export load_template_data() - called during static site generation to inject dynamic data. For example, src/public/index.ts provides the homepage's services, testimonials, and product data by language:
export async function load_template_data(): Promise<Record<string, any>> {
return {
services: { en: [...], sl: [...] },
testimonials: { en: [...], sl: [...] },
home: { ... },
};
}
The returned data is merged into the render context and accessed via props.xxx in templates.
Translation Files ({lang}.json)
Each language gets a .json file alongside templates. Translation keys are merged from the directory hierarchy - a key in src/public/en.json is available globally, while a key in src/public/blog/en.json overrides it for the blog section. The i18n.ts loader handles cross-language fallback: missing keys inherit from other languages (except route_name, which is always language-specific).
The src/components/ Directory
src/components/ holds reusable .ree partials. A fresh ReeWeb project includes three starter components - banner.ree (status notifications), my-h1.ree (styled heading), and speculation-rules.ree (instant navigation via the browser's Speculation Rules API). Add your own by dropping .ree files into this directory.
You include a component by writing it as a custom HTML element whose tag matches the file name:
<banner type="green" text="Saved!"></banner> <my-h1>Hello</my-h1>
The template engine resolves <banner> to src/components/banner.ree automatically via the $components/ alias. Attributes arrive as props.attributes and slot content as props.children. See Components.
The Lib Directory
Plain TypeScript modules with zero runtime dependencies:
| File | Purpose |
|---|---|
lib/template_engine.ts | Orchestrates .ree rendering (load, render, cache); delegates compilation to lib/template/ (compiler.ts, custom_elements.ts, include_handler.ts, include_resolver.ts, types.ts) |
lib/template_helpers.ts | Template helper functions (localized_path, nav_label, key_values, date formatters, currency, etc.) |
lib/collect_records.ts | Content collection - walks markdown files in a route directory, parses frontmatter, filters visibility, sorts and paginates |
lib/content_visibility.ts | Per-page visibility model: default_visibility(frontmatter, now) returns render/list/feed/sitemap/index booleans. Draft and date-based publication logic. |
lib/hooks.ts | Project hooks contract - typed ProjectHooks interface for scripts to call at fixed points. See Project Hooks. |
lib/pagination.ts | Pure pagination view-model builder - given a record count and config, produces PaginationData for the <full-pagination> / <simple-pagination> components |
lib/i18n.ts | Translation file loader - walks directories, discovers {lang}.json files, builds merged translations with cross-language fallback |
lib/images.ts | Responsive-image URL helpers - srcset() generates <picture> srcset attribute from width-stepped variants |
lib/markdown_docs.ts | Generic markdown post-processor pipeline - TOC scan, syntax highlighting, external-link handling, class injection. Style-free; Tailwind classes come from src/lib/markdown_styles.ts (do not edit the pipeline) |
lib/static_site.ts | Static SSG helpers - walk_dir, parse_frontmatter, template_to_canonical, build_static_route_map, collect_page_files |
lib/route_aliases.ts | URL slugification - slugify() transliterates Unicode to ASCII for localized route URLs |
lib/redirects.ts | Redirect validation and emission - schema validation, collision checks, HTML stubs + _redirects file |
The Config Directory
| File | Purpose |
|---|---|
config/supported_languages.ts | Language list, locale mappings, default language, soft launch list |
config/redirects.ts | URL redirects (301/302) - emitted as dist/_redirects + HTML stubs |
config/pagination.ts | Pagination config - routes, items per page, URL segment, behaviour |
The Scripts Directory
| Script | Purpose |
|---|---|
scripts/ssg.ts | Static site generator - renders templates + markdown, copies assets, emits redirects, generates paginated routes |
scripts/dev.ts | Development server - serves pages with live reload via WebSocket |
scripts/preview.ts | Preview server for dist/ - a custom Bun HTTP server, not bun x serve |
scripts/install.ts | Install task - initialises Git, copies .env, installs dependencies, and fetches development tooling and vendor assets |
scripts/generate_sitemap.ts | Generates sitemap.xml |
scripts/generate_rss.ts | Generates RSS feed from blog markdown |
scripts/prepare_images.ts | Generates responsive image variants (WebP/JPEG) from assets directory |
The SSG Flow (scripts/ssg.ts)
The SSG script runs in phases:
- Schema-validate redirects - catch config errors early
- Clear and recreate
dist/- starts fresh - Load translations - walks
src/public/for{lang}.jsonfiles - Collect files - discovers
.reetemplates,.mdfiles, and static assets - Load dynamic data - calls
load_template_data()from sibling.tsfiles - Generate route map - resolves canonical → localized paths per language
- Render templates - each
.reefile rendered per language with merged translations - Generate sidebar navigation - from markdown frontmatter
has_sidebar: true - Render markdown - each
.mdfile rendered throughprocess_docs_markdown. Content visibility (draft/published date) is evaluated here - unpublished pages are generated but hidden from listings, feeds, and sitemaps. - Render paginated routes - routes listed in
config/pagination.tsget numbered page output (e.g./blog/,/blog/2/,/blog/3/) - Copy static files - images, fonts, JS, etc. copied verbatim
- Emit redirects -
dist/_redirects+ HTML stubs - Summary - total rendered, errors, static files copied