Project structure
Pages map to routes. Everything else is a convention.
Server pages live under pages/. Client components can live anywhere else - components/ and islands/ are conventions, not rules.
Routes from files
The directory tree maps directly to URL routes:
| File | Route |
|---|---|
src/pages/Index.tsx | / |
src/pages/About.tsx | /about |
src/pages/blog/Post.tsx | /blog/post |
src/pages/docs/Install.tsx | /docs/install |
Nesting is unlimited and compiled to depth-correct relative paths, so a page at /blog/post loads its runtime, islands, and stylesheets from ../-levels - never from the root, and never with an assumption about where the site is mounted.
// @runsOn server, // @runsOn client, or // @runsOn api directive. This is the framework's only mechanism for the server/client/api boundary - there is no inference from filenames or import sites. Page metadata: the head const
A server page declares page-level metadata by assigning a raw HTML string to a const head at the top of the component body. It is injected verbatim into the built page's - no parsing, no escaping, no transformation. Use it for , , Open Graph tags, or structured data:
const head = 'My Page '; The name is fixed: head. A page that declares no head gets only the import map and its CSS links.
Conventions, not rules
pages/- the only directory with routing meaning for pages.api/- top-level directory mapped to/api/*HTTP routes.components/- server-rendered building blocks imported by pages.islands/- client components hydrated atclient:hydratecall sites.middleware.ts- optional; one request gate for the whole site (project root).src/site.css- a shared stylesheet imported by a component every page renders.
The compiler walks each page's component tree to find its islands and stylesheets, so files outside pages/ are only included when a page actually uses them.