Capy-Bearer Documentation

13. Coming from React

If you have written React, most of what you know transfers — the parts that change are where rendering happens and what a component is allowed to hold.

Capy renders on the server. A unit is a .capy file, a page is a function RENDER, and the output is HTML written with print. There is no virtual DOM, no client bundle, and no file-system router unless you write one.

The mapping

ReactCapy
a page componentfunction RENDER(request : dval)
a reusable componentfunction COMPONENT(request : dval)
several components in one filefunction COMPONENT:NAME(request : dval)
propsrequest.props
return <div>…</div>print(…)
rendering a childcomponent_render("card") or component("card")

A handler returns nothing. Whatever it prints becomes the response, so the React habit of building a tree and returning it becomes writing output in order:

function RENDER(request : dval) {
    var name := string(request.query.name, {fallback: "guest"})
    print("<main><h1>Hello, ", name, "</h1>")
    print("<p>Rendered on the server.</p></main>", "\n")
}

Output

<main><h1>Hello, guest</h1><p>Rendered on the server.</p></main>

Components take explicit props

A React component receives props as an object and can render children. A Capy component receives request.props and has no children or slots — if a fragment needs inner content, pass it as a prop or call another component yourself.

function COMPONENT(request : dval) {
    var title := string(request.props.title, {fallback: "Untitled"})
    print("<article><h3>", title, "</h3></article>")
}

The caller decides how the result is used. component_render() writes the fragment straight into the response, which is the common case in a layout. component() returns it as a string when you need to wrap, measure, or conditionally emit it.

Props are scoped to the call and restored when it returns, so a component cannot disturb its caller's props and nested components each read their own.

There is no client state

This is the biggest adjustment. There is no useState, no effect hook, and no re-render — a handler runs once per request and produces final HTML.

What replaces them:

  • per-request shared staterequest.call, readable by every handler in the request

  • work that runs once before a unit's handlersfunction ONCE(request : dval)

  • work that runs once when a worker loads a unitfunction INIT(request : dval)

  • state that outlives the request — a session, a file, a database, or a task

A module handle from unit_load() lives only for the current request; do not stash one for later.

Escaping

HTML literals escape interpolated values, like React escapes {value} by default:

function RENDER(request : dval) {
    var supplied := string(request.query.q, {fallback: "<script>"})
    print(<><p><?= supplied ?></p></>)
}

Use html_escape() when you are building a string by concatenation instead.

What Bearer deliberately does not have

No asset registry, no island registry, no JSX-style component tags, no built-in children or slots. Routing is application code — the starter example checks page files and parent index handlers, which keeps the rules visible rather than implied by directory layout.

When something does not compile

Bearer reports the source path and the compiler output, and writes the full output under BIN_DIRECTORY. A compile error names the unit and line, not a bundler stack.