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:

FilePurpose
lib/template_engine.tsOrchestrates .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.tsTemplate helper functions (localized_path, nav_label, key_values, date formatters, currency, etc.)
lib/collect_records.tsContent collection - walks markdown files in a route directory, parses frontmatter, filters visibility, sorts and paginates
lib/content_visibility.tsPer-page visibility model: default_visibility(frontmatter, now) returns render/list/feed/sitemap/index booleans. Draft and date-based publication logic.
lib/hooks.tsProject hooks contract - typed ProjectHooks interface for scripts to call at fixed points. See Project Hooks.
lib/pagination.tsPure pagination view-model builder - given a record count and config, produces PaginationData for the <full-pagination> / <simple-pagination> components
lib/i18n.tsTranslation file loader - walks directories, discovers {lang}.json files, builds merged translations with cross-language fallback
lib/images.tsResponsive-image URL helpers - srcset() generates <picture> srcset attribute from width-stepped variants
lib/markdown_docs.tsGeneric 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.tsStatic SSG helpers - walk_dir, parse_frontmatter, template_to_canonical, build_static_route_map, collect_page_files
lib/route_aliases.tsURL slugification - slugify() transliterates Unicode to ASCII for localized route URLs
lib/redirects.tsRedirect validation and emission - schema validation, collision checks, HTML stubs + _redirects file

The Config Directory

FilePurpose
config/supported_languages.tsLanguage list, locale mappings, default language, soft launch list
config/redirects.tsURL redirects (301/302) - emitted as dist/_redirects + HTML stubs
config/pagination.tsPagination config - routes, items per page, URL segment, behaviour

The Scripts Directory

ScriptPurpose
scripts/ssg.tsStatic site generator - renders templates + markdown, copies assets, emits redirects, generates paginated routes
scripts/dev.tsDevelopment server - serves pages with live reload via WebSocket
scripts/preview.tsPreview server for dist/ - a custom Bun HTTP server, not bun x serve
scripts/install.tsInstall task - initialises Git, copies .env, installs dependencies, and fetches development tooling and vendor assets
scripts/generate_sitemap.tsGenerates sitemap.xml
scripts/generate_rss.tsGenerates RSS feed from blog markdown
scripts/prepare_images.tsGenerates responsive image variants (WebP/JPEG) from assets directory

The SSG Flow (scripts/ssg.ts)

The SSG script runs in phases:

  1. Schema-validate redirects - catch config errors early
  2. Clear and recreate dist/ - starts fresh
  3. Load translations - walks src/public/ for {lang}.json files
  4. Collect files - discovers .ree templates, .md files, and static assets
  5. Load dynamic data - calls load_template_data() from sibling .ts files
  6. Generate route map - resolves canonical → localized paths per language
  7. Render templates - each .ree file rendered per language with merged translations
  8. Generate sidebar navigation - from markdown frontmatter has_sidebar: true
  9. Render markdown - each .md file rendered through process_docs_markdown. Content visibility (draft/published date) is evaluated here - unpublished pages are generated but hidden from listings, feeds, and sitemaps.
  10. Render paginated routes - routes listed in config/pagination.ts get numbered page output (e.g. /blog/, /blog/2/, /blog/3/)
  11. Copy static files - images, fonts, JS, etc. copied verbatim
  12. Emit redirects - dist/_redirects + HTML stubs
  13. Summary - total rendered, errors, static files copied