Examples
Compare BTSX input with native TSRX output — all goldens are compiled and validated, shown here without leaving the docs.
Every example below is a tested golden from examples/ — the BTSX source is exactly what you author, the TSRX is the readable output Beast generates before Octane validates it. Copy either file and run it with beastOctane() in Vite.
- BTSX uses indentation for structure — no closing tags
- Control flow (
if,each/empty,switch,try/pending/catch) compiles to native Octane@if/@for/@switch/@try - Use
className="..."for Tailwind utilities (class="..."also works, normalized toclassName)
Basic props, indentation, conditions, and keyed iteration.
props { user, unreadCount, messages }: { user: { name: string; id: string; isAdmin: boolean }; unreadCount: number; messages: { id: string; text: string }[] }.card .header h1 Welcome, #{user.name} .body if user.isAdmin AdminPanel(userId={user.id}) else p You have #{unreadCount} new messages ul.messages each message, i in messages li.message(key={message.id}) #{message.text}export default function Card({ user, unreadCount, messages,}: { user: { name: string; id: string; isAdmin: boolean }; unreadCount: number; messages: { id: string; text: string }[];}) @{ <div className="card"> <div className="header"> <h1>Welcome, {user.name}</h1> </div> <div className="body"> @if (user.isAdmin) { <AdminPanel userId={user.id} /> } @else { <p>You have {unreadCount} new messages</p> } <ul className="messages"> @for (const message of messages; index i; key message.id) { <li className="message">{message.text}</li> } </ul> </div> </div>}Setup with Octane hooks, useState/useMemo/useEffect, and event handlers.
import { useEffect, useMemo, useState } from "octane";props { initialCount, step, onCountChange }: { initialCount: number; step: number; onCountChange: (count: number) => void }setup const [count, setCount] = useState(initialCount);setup const doubled = useMemo(() => count * 2);setup useEffect(() => onCountChange(count)); section.counter(aria-live="polite") h2 Hook counter p Current: #{count} p Doubled: #{doubled} .actions button(type="button" onClick={() => setCount(count - step)}) Decrease button(type="button" onClick={() => setCount(count + step)}) Increaseimport { useEffect, useMemo, useState } from "octane"; export default function Counter({ initialCount, step, onCountChange,}: { initialCount: number; step: number; onCountChange: (count: number) => void;}) @{ const [count, setCount] = useState(initialCount); const doubled = useMemo(() => count * 2); useEffect(() => onCountChange(count)); <section className="counter" aria-live="polite"> <h2>Hook counter</h2> <p>Current: {count}</p> <p>Doubled: {doubled}</p> <div className="actions"> <button type="button" onClick={() => setCount(count - step)}>Decrease</button> <button type="button" onClick={() => setCount(count + step)}>Increase</button> </div> </section>}Keyed loops, empty fallback, and attribute merging (class, data-*, disabled).
props { products, selectedId, onSelect }: { products: { id: string; name: string; price: number; featured: boolean }[]; selectedId: string | null; onSelect: (id: string) => void } section#catalog.catalog(aria-label="Product catalog") h2 Products ul.product-grid each product, index in products key product.id li.product-card(className={product.id === selectedId ? "selected" : ""} data-index={index}) if product.featured span.badge Featured h3 #{product.name} p.price $#{product.price.toFixed(2)} button(type="button" formNoValidate disabled={product.id === selectedId} onClick={() => onSelect(product.id)}) Select empty li.empty-state No products available.export default function Catalog({ products, selectedId, onSelect,}: { products: { id: string; name: string; price: number; featured: boolean }[]; selectedId: string | null; onSelect: (id: string) => void;}) @{ <section id="catalog" className="catalog" aria-label="Product catalog"> <h2>Products</h2> <ul className="product-grid"> @for (const product of products; index index; key product.id) { <li className={[product.id === selectedId ? "selected" : "", "product-card"].filter(Boolean).join(" ")} data-index={index}> @if (product.featured) { <span className="badge">Featured</span> } <h3>{product.name}</h3> <p className="price">${product.price.toFixed(2)}</p> <button type="button" formNoValidate disabled={product.id === selectedId} onClick={() => onSelect(product.id)}>Select</button> </li> } @empty { <li className="empty-state">No products available.</li> } </ul> </section>}Scoped <style> with :global() escape — works alongside Tailwind className="...".
module interface StylingProps { title: string; cardProps: Record<string, unknown> }props { title, cardProps }: StylingProps fragment article.card({...cardProps}) h2 #{title} p Scoped styling follows this component. style .card { padding: 1rem; } .card h2 { color: rebeccapurple; } :global(body) { margin: 0; }interface StylingProps { title: string; cardProps: Record<string, unknown> } export default function Styling({ title, cardProps }: StylingProps) @{ <> <article className="card" {...cardProps}> <h2>{title}</h2> <p>Scoped styling follows this component.</p> </article> <style> .card { padding: 1rem; } .card h2 { color: rebeccapurple; } :global(body) { margin: 0; } </style> </>}Dotted provider Theme.Provider, local component declarations, and use/useContext.
import { createContext, use, useContext } from "octane";module type ThemeName = "light" | "dark"; interface ProviderProps { theme: ThemeName; children: unknown; } const Theme = createContext<ThemeName>("light"); component ThemeLabel setup const theme = use(Theme); p.theme-label #{"Current theme: " + theme} component ThemeSwatch setup const theme = useContext(Theme); span(aria-label={"Theme swatch: " + theme} data-theme={theme})props { theme, children }: ProviderProps Theme.Provider(value={theme}) section.theme-shell(data-theme={theme}) ThemeLabel ThemeSwatch | #{children}import { createContext, use, useContext } from "octane";type ThemeName = "light" | "dark";interface ProviderProps { theme: ThemeName; children: unknown;} const Theme = createContext<ThemeName>("light"); function ThemeLabel() @{ const theme = use(Theme); <p className="theme-label">{"Current theme: " + theme}</p>} function ThemeSwatch() @{ const theme = useContext(Theme); <span aria-label={"Theme swatch: " + theme} data-theme={theme} />} export default function Provider({ theme, children }: ProviderProps) @{ <Theme.Provider value={theme}> <section className="theme-shell" data-theme={theme}> <ThemeLabel /> <ThemeSwatch /> {children} </section> </Theme.Provider>}try/pending/catch with reset — compiles to Octane @try/@pending/@catch.
props { Profile, data }: { Profile: (props: { data: Promise<unknown> }) => unknown; data: Promise<unknown> } section.profile-boundary try Profile(data={data}) pending p(role="status") Loading profile… catch error, reset .error(role="alert") p Could not load profile: #{error instanceof Error ? error.message : String(error)} button(type="button" onClick={reset}) Try againexport default function Boundary({ Profile, data,}: { Profile: (props: { data: Promise<unknown> }) => unknown; data: Promise<unknown>;}) @{ <section className="profile-boundary"> @try { <Profile data={data} /> } @pending { <p role="status">Loading profile…</p> } @catch (error, reset) { <div className="error" role="alert"> <p>Could not load profile: {error instanceof Error ? error.message : String(error)}</p> <button type="button" onClick={reset}>Try again</button> </div> } </section>}Multiple roots, pipe text, and symbol escaping.
props { heading, count }: { heading: string; count: number } // Multiple roots compile to a native TSRX fragment.h1 #{heading}| This view intentionally has multiple roots.p.notice You have #{count} pending items.| Symbols stay safe: < > { } &.export default function Fragment({ heading, count,}: { heading: string; count: number;}) @{ <> <h1>{heading}</h1> This view intentionally has multiple roots. <p className="notice">You have {count} pending items.</p> Symbols stay safe: < > { } &. </>}switch/case/default — compiles to @switch/@case/@default.
props { state }: { state: "idle" | "loading" | "ready" | "error" } section.status-card(aria-live="polite") switch state case "idle" p Choose an action. case "loading" p(role="status") Loading… case "ready" p Ready. default p(role="alert") Something went wrong.export default function Variant({ state,}: { state: "idle" | "loading" | "ready" | "error";}) @{ <section className="status-card" aria-live="polite"> @switch (state) { @case "idle": { <p>Choose an action.</p> } @case "loading": { <p role="status">Loading…</p> } @case "ready": { <p>Ready.</p> } @default: { <p role="alert">Something went wrong.</p> } } </section>}All 22 goldens follow the same BTSX → TSRX rule and are validated by bun run check in the Beast repo.
| Golden | Highlights |
|---|---|
| actions | useActionState, useOptimistic, useFormStatus, requestFormReset |
| app | Top-level App.btsx (typed Props) |
| async | Async boundaries |
| deferred | Deferred values |
| editor | useLinkedState + onInput |
| hooks | useState/useEffect patterns |
| library | Library packaging |
| network | Data fetching |
| portal | Portals |
| refs | Object/callback ref arrays |
| responsive | Responsive style |
| shortcut | useEffect cleanup + block setup |
| status | Groups + conditional badges |
| transitions | View transitions |
8 compelling Beast demos.
check it out| Title | Highlight |
|---|---|
| Canvas | Real-Time Collaborative Canvas |
| Form | Adaptive Form with Validation Streams |
| Grid | Virtualized Masonry Grid |
| Shell | Isomorphic Shell with Progressive Hydration |
| Timeline | Timeline — Time as Dimension |
| Worker | Worker — Zero-Cost Threading |
| Motion | Motion — Animation Orchestrator |
| Streaming | Streaming — Edge at CDN |
| Table | Tanstack Table = Nuqs - shadcn filters |