Cloudflare: Routing & components
File-system routing
Section titled “File-system routing”| Astro | phaze-cloudflare | |
|---|---|---|
| Routes scanned from | src/pages/** | src/pages/** (pages + content endpoints + co-located layouts) + src/api/** (HTTP handlers) |
| Page extension | .astro / .tsx / .md / .mdx | .tsx / .jsx / .phaze |
| Routable basenames | every file routes; _* prefix opts out | strict allowlist — only index / page / layout (.tsx/.jsx/.phaze) OR <name>.<content-ext>.ts for content endpoints; everything else is a co-located helper, never publicly addressable |
| HTTP handler dir | mixed with pages (src/pages/api/) | separate top-level dir (src/api/**, URL auto-prefixed with /api/) |
| Dynamic segments | [id], [...rest] as basename OR folder | [id]/page.tsx, [...rest]/page.tsx — folder-only (dynamic basename shorthand removed under strict allowlist) |
| Co-located layouts | n/a | pages/<dir>/layout.{tsx,jsx,phaze} auto-pairs with sibling page (same-folder only) |
| Specificity sort | yes | yes |
| Page + API collision | n/a (.astro vs .ts differ) | impossible by directory separation (src/pages/ vs src/api/); within-dir duplicates throw at discovery |
Advantage: the strict allowlist closes the “every file is a route” risk (internal-debug.ts, OldThing.tsx, notes.tsx are never accidentally publishable). Helpers co-locate freely under src/pages/ without becoming URLs. The page/API split by directory makes the mental model explicit (“pages/ for what users see; api/ for what services call”).
Component model
Section titled “Component model”| Astro | phaze-cloudflare | |
|---|---|---|
| Top-level page | .astro template with fenced frontmatter | TSX function component, default export |
| Reactive islands | <Component client:load/> / client:idle / client:visible / client:only="phaze" | Whole page is one reactive tree; no island gating |
| Shared layout | Layout .astro files + <slot/> + named <slot name="X"/> | Component imported + children + <Fragment slot="X">…</Fragment> extracted by phaze-compile |
| Slot lazy contract | eagerly serialised into the parent’s HTML | phaze-compile wraps named-slot children in () => … arrows — layout decides when to evaluate; lets layouts ship without computed |
| Multi-child JSX wrap | n/a | emits arrayExpression instead of JSXFragment when wrapping reactive multi-child — keeps the Fragment symbol out of phaze |
<For> runtime | n/a (lists use .map() in islands) | default <For for:item={items}> compile-strips to {() => items().map(...)} — zero shipped For bytes. phaze flag opts into the runtime For (~900 B) when row identity has to survive reorders. See <For> reference. |
Advantage: the island / page split disappears. Astro emits a small per-island hydration shim (~96 B + ~66 B brotli per client:only directive); phaze-cloudflare’s whole page hydrates from one entry. The trade-off is no static-only zones inside a page — the entire page is reactive. For form-heavy and CRUD-style apps this is the correct trade.