.phaze file format
What .phaze is
Section titled “What .phaze is”.phaze is an opt-in source file format that organizes a page or component’s source into named boundaries so a reader knows what to expect from each region of the file. It compiles to a normal .tsx module — every existing phaze-compile pass (/dsl, /match, /numeric, /list, /time, defer:, transition:, class:, bind:, etc.) applies inside the boundaries unchanged. The runtime contract is identical to writing the equivalent .tsx by hand.
The win is DX, not capability. .phaze doesn’t enable anything .tsx can’t already do — it makes the structure of a typical page (page-level metadata, server-side data, module-scope reactive state, component body) visible at a glance instead of dispersed across named exports and module-scope code.
.phaze files coexist with .tsx in the same src/pages/** tree. The choice is per-file.
The two modes
Section titled “The two modes”A .phaze file has exactly one mental dispatch:
Has ---page? | Mode | What it is |
|---|---|---|
| Yes | Page | A routed page — appears in src/pages/**, gets routing, caching, server-side data loading |
| No | Component | A reusable component — used anywhere; takes props |
No ---comp label — absence of ---page is what marks a file as a component. The label asymmetry is deliberate: pages are the special case (one prop signature { data }, compiler-known); components are the default (any prop signature, user-defined).
The named boundaries
Section titled “The named boundaries”Four optional named regions in this conventional order (when present), plus a trailing component body:
---page // opt-in — marks this file as a page // …page-level contract + imports…---data // opt-in — server-side data; pages only // …loader body…---state // opt-in — Local and @global state inventory // …state declarations…---props // opt-in — component input inventory; components only // …prop declarations…--- // REQUIRED — marks the trailing region boundary
// …trailing component body… (implicit for pages, explicit arrow for components)Each boundary has a single semantic intent:
| Boundary | Semantic | Compiles to |
|---|---|---|
---page | ”What the page declares about itself.” | Named exports: head, headers, revalidate, layout, prerender |
---data | ”What the server does with this request before rendering.” (Pages only) | export const loader = async (pageCtx) => … |
---state | ”This component’s signal inventory — Local by default, @global to share across files.” | const declarations: Local hoisted into the body; @global becomes export const in app.phaze (auto-imported elsewhere) |
---props | ”What this component receives.” (Components only) | Destructured arrow params + TypeScript type literal — synthesizes ({…}: {…}) => |
| Trailing region | ”The component itself.” | export default function Page({ data }) { … } (pages) or export default (props) => … (components) |
Two further boundaries — ---platform and ---cloudflare — sit outside this core set: they’re an edge-data subsystem that compiles to instance-scope edgeSignals rather than exports / loader / state, and they list bare field names instead of name: value. See Edge data.
Boundary stacking — the canonical form
Section titled “Boundary stacking — the canonical form”Consecutive boundaries stack: a ---<label> line while a boundary is already open implicitly closes the previous boundary and opens the new one. The only fence that explicitly closes is the final bare ---, which transitions from the boundary block into the trailing region.
---page // …page content…---data // implicitly closes ---page // …data content…---state // implicitly closes ---data // …signals content…--- // REQUIRED — closes ---state AND opens the trailing region
// …trailing region…Why the final --- is required. Without it, the parser can’t tell where ---state ends and the trailing region begins — both contain JS statements that may include const X = s(…) declarations (Local in ---state, hoisted into the component body; per-render in the trailing region’s JSX expression). The bare --- is the one syntactic token that exits the boundary stack — a cheap visual cost for a real grammar guarantee.
The unstacked form is also valid. Each boundary closed by its own --- works identically:
---page // …---
---data // …---
---state // …---
// …trailing region…Stacked is canonical; unstacked is supported for migration and reader preference. Linters can normalize to stacked.
Parser rule
Section titled “Parser rule”| Line shape | Action |
|---|---|
---page / ---data / ---state / ---props | If a boundary is open, close it. Open the new one. |
--- (bare) | If a boundary is open, close it. Transition to trailing region. |
| Anything else | Append to the currently open region (boundary or trailing). |
What the format does NOT change
Section titled “What the format does NOT change”.phaze is a source-syntax layer. Nothing at the runtime layer changes:
- The PageModule contract is unchanged — the compiler emits
export const head/loader/headers/revalidate/layoutexports verbatim. - Phaze’s reactivity model is unchanged — signals are signals, computeds are computeds, the SSR/hydrate path is the same.
- The phaze-cloudflare router is unchanged —
currentRoute()is the same signal; the swap mechanism is the same. - The action surface is unchanged —
actions.X(input)per-call-site fetch arrow inlining is the same. - Bundle cost is zero at the framework level —
.phazeis build-time only.
The same is true of every compile-strip subpath (/dsl, /match, /numeric, /list, /time’s duration helpers) — all of them work inside .phaze boundaries verbatim because the compiler runs them after lowering .phaze to .tsx.
File extension on the filesystem
Section titled “File extension on the filesystem”| File | Extension | Mode |
|---|---|---|
Page (---page present) | .phaze under src/pages/** | Page |
Component (no ---page) | .phaze anywhere | Component |
Page that doesn’t want .phaze sugar | .tsx under src/pages/** | Plain PageModule, no boundaries |
Component that doesn’t want .phaze sugar | .tsx anywhere | Plain function component |
.phaze and .tsx coexist — the choice is per-file. A project can adopt .phaze gradually, file-by-file.
See also
Section titled “See also”Phaze Router— the runtime contract.phazecompiles toPage Anatomy— the five PageModule exports that---pageand---dataproduceDSL & directives— the compile-strip helpers (s,c,watch,phaze,s.async).phazefiles should use throughoutPhaze + Cloudflare— the adapter that hosts.phazepages
In this section
Section titled “In this section”- .phaze: Boundaries —
---page,---data,---state(with@global+ auto-import),---props, the trailing region, and dynamic page titles - .phaze: Edge data (Cloudflare) — the
---platform/---cloudflareedge-data boundaries that compile toedgeSignals - .phaze: Structure & state placement — layouts & slots, the state placement decision tree, and anti-patterns
- .phaze: Examples & pipeline — full page and component examples, the compile pipeline, host-adapter wiring, and the TypeScript LSP