CAKE20

AI Web PaaS

Pages and Layout

Configure Cake20 View screens with Cake20 file-based routing.

Page URL

  • app/pages/index.tsx → /
  • app/pages/about.tsx → /about
  • app/pages/users/[id].tsx → /users/:id
  • app/pages/docs/[...slug].tsx → /docs/*
  • If you have a lot of files, organize them into folders as deep as you need, like app/pages/admin/users/*.tsx.

Cake20 View syntax

Cake20 View expresses screen behavior with TypeScript conditions, array map, standard events, and bind.

  • Use class for styles, exactly as in HTML.
  • Use standard event names such as onClick and onInput.
  • Use TypeScript conditions and array map for conditional and repeated output.
  • Use map only on arrays. For numeric repetition, use Array.from({ length: count }).map(...).
  • Use bind for two-way values.
  • Pass an event function directly or call it. Do not leave a callback that only references the function name.
export const data = {
  open: true,
  name: "",
  items: ["strawberry", "chocolate"]
};

export default () => (
  <main class="space-y-3">
    <Input bind={data.name} />
    <Button onClick={() => (data.open = !data.open)}>Toggle</Button>
    {data.open && <Panel />}
    {data.items.map((item) => <p key={item}>{item}</p>)}
  </main>
);

Save and compile

@cake20/view compiles saved TSX into per-instance reactive state and screen code.

  • Reserved exports include data, computed, watch, and lifecycle functions.
  • class, html, bind, and event options are converted into screen runtime rules.
  • Syntax errors stop save or build and report the exact source location.

Cake20.js migration checks

  • Do not reference data or another declaration before it is initialized.
  • Import ordinary module APIs such as state and element from their documented paths. Do not confuse them with component auto-imports.
  • Validate array and object shapes before assigning API responses to data. Do not erase useful defaults with an empty Preview response.
  • Use RouterLink for Cake20 screen navigation.
  • After migration, verify every route, key button and input, two-way binding, login restoration, and API error path instead of checking only the first render.

Code format when saving

When you press the save button or save shortcut in the editor, the support files are organized according to Cake20's internal Oxfmt rules and saved, and the results are immediately reflected in the current tab.

  • Supports TypeScript, TSX, JavaScript, HTML, CSS, and JSON text files.
  • It goes through existing change conflict checks and build locks, and if formatting fails, it is not saved.
  • Website-specific configuration files are not used. The same Cake20 internal rules apply to all websites.
  • SVG, images and Prisma schema files are not eligible for code formatting.

Specify Layout

export const config = {
  layout: "admin"
};

export default () => <h1>Management screen</h1>;

app/layouts/admin.tsx wraps the page. You can turn off the layout with layout: false.

Layout Preview

  • When you select a layout file, it places a virtual box labeled slot instead of an actual page.
  • If there are props in the layout, enter the preview value in sample.
  • If there is no sample, only the layout structure is displayed with empty props.
export const sample = {
  sidebar: true
};

Automatic registration of components

  • Cake20 View files in app/components are used in pages, layouts, and other components without import.
  • The .client suffix in *.client.ts and *.client.tsx marks browser-only source and is not part of the component name. Use Chart.client.tsx as <Chart />.
  • If there are many files, the depth you need, such as app/components/account/profile/*.tsx, Organize into folders.
  • Create component names by combining the group folder and file names in PascalCase.
  • Files with the same auto-registration name cannot be placed together.
  • For a named two-way component value, use bindName and match the target name prop with onNameChange or onUpdate:name. Multiple named binds may be used on one tag.
// app/components/account/ProfileCard.tsx
export type Props = { children?: View };
export default (props: Props) => <Card>{props.children}</Card>;

// app/pages/index.tsx
export default () => <AccountProfileCard>Profile</AccountProfileCard>;

Separate pages and components

Pages and layouts control the overall screen flow, data connection, and component combination. Contains, independent screen areas, and repetitive UI using Cake20 View of app/components. Separate into components.

  • When roles are separated, such as multiple sections, cards, lists, forms, and modals, they can be organized into one page file. Instead of stacking all your markup and logic, divide them into components for each responsibility.
  • If the same UI is repeated more than once or has its own props, state or events It is based on separation into components.
  • The page file keeps routing and screen combinations visible at a glance. There is no need to break it down into a few simple tags.
  • If the screen state and communication logic get long, default to app/stores/<name>.store.ts Pure functions and types used by the UI and server are shared/utils and Separate by shared/types.
  • Reusable screen logic in app/composables and its subfolders is automatically imported, but Cake20's official templates use stores by default.
  • If an existing file already has multiple responsibilities, consider deleting the relevant areas before adding more functionality. First, organize it into component·store·common type.
  • The template is not a one-time demo, but the user downloads it, learns it, and AI takes over. It is considered an operating source that you modify and is subject to the same separation criteria.
  • The API file focuses on input validation, authorization checks and response linking, and contains several APIs. Shared pure functions and types are separated into shared/utils and shared/types.
  • Once the static HTML is large enough to contain multiple screen areas and interactions, the Cake20 View page and Move to components and use HTML files for small, independent static documents.
// app/pages/orders.tsx: data connection and page composition
export const data = { orders: [] as Order[] };
export const onServer = async () => {
  data.orders = await fetchGet<Order[]>("/api/orders");
};
export default () => (
  <>
    <OrderSummary orders={data.orders} />
    <OrderTable orders={data.orders} />
    <OrderForm onSaved={refresh} />
  </>
);

// app/components/OrderTable.tsx: list display only
export type Props = { orders: Order[] };
export default (props: Props) => <Table data={props.orders} />;

Result

You can find the file for the feature you want to modify just by its name, reducing the need for AI to read or replace irrelevant screens.

Provide Context to descendants

  • The parent component's export const context provides context to descendants.
  • A descendant uses useContext<Value>(name) to find the nearest matching context value.
  • useContext() returns a getter, so call it as theme(). Provide changing values as () => data.theme to preserve reactive updates.
  • If an intermediate component provides the same name, its descendants use the nearer value.
  • Use props for directly connected parents and children. Use context for Form, Tabs, Editor, theme, and other context scoped to a UI subtree.
  • Use a store for global state, state shared across unrelated screen areas, and persistent state.
  • Context lives only in the component tree's memory, so it is cleared on reload or when the providing component is unmounted.
// app/components/ThemePanel.tsx
export const data = {
  theme: "dark" as "light" | "dark"
};

export const context = {
  theme: () => data.theme
};

export default () => (
  <section>
    <Button onClick={() => {
      data.theme = data.theme === "dark" ? "light" : "dark";
    }}>Toggle theme</Button>
    <ThemeLabel />
  </section>
);

// app/components/ThemeLabel.tsx
const theme = useContext<"light" | "dark">("theme");

export default () => (
  <p>{theme() === "dark" ? "Dark theme" : "Light theme"}</p>
);

Result

ThemeLabel uses the reactive theme from the nearest ThemePanel without receiving a prop.

Component Preview

  • Selecting a component file displays only the component without the page and layout.
  • Pass export const sample as component props.
  • If there are required props, enter valid test values ​​in the sample as well.
  • A sample is an executable document that provides previews, type validation, and usage examples.
export type Props = {
  title: string;
  count: number;
};

export const sample = {
  title: "today's visitors",
  count: 1280
} satisfies Props;

export default (props: Props) => (
  <Card>{props.title}: {props.count.toLocaleString()}</Card>
);