RouterPRISM routing
PRISM routing

This tutorial uses PRISM file-based routing: every folder under src/app/ becomes a URL, with nested layouts and server-side loaders.

Read the routing docs →
UIbext/ui
bext/ui

Components come from bext/ui — a faithful shadcn/ui port for bext and PRISM, styled through route_css tokens.

Explore bext/ui →
FREN
Step 7 of 11 · Show some data

The products table

Load data in a loader and render it as a table — all on the server, no client fetch.

Now the data. A loader runs on the server before the page renders; whatever it returns arrives as the page's data prop. Because it runs server-side, you can read a database, call an API, or — here — just sort an array.

src/app/products/page.tsx
export async function loader() {
  const products = [...CATALOGUE].sort((a, b) => a.name.localeCompare(b.name));
  return { products };
}

export default function Products({ data }) {
  return (
    <table>
      {data.products.map((p) => (
        <tr key={p.id}><td>{p.name}</td><td>${p.price}</td></tr>
      ))}
    </table>
  );
}

There's no fetch on the client and no loading spinner — the HTML arrives complete, with the rows already in it. The full table (with SKU and a stock badge) is rendering live in the preview.

NOTE
To talk to a real database, bext bundles SQLite — query it from the loader via the bridge. Reading query params for filtering/pagination? The loader gets the request; see data & loaders.
Files
src
app
products
src/app/products/page.tsxread-only
localhost:3000