Loading...
Loading...
Ossido 0.1.3 ships two big additions: server actions for typed mutations
across the Rust/React boundary, and a real V8 event loop that makes render-time
async - Suspense, use(), React.lazy, top-level await - actually resolve
during server rendering and static export.
#[handler] loads the data a page needs. Server actions are its counterpart: write
a Rust function to do something - create, update, delete, submit a form - and
the build mirrors it into a typed TypeScript function you call from React. The
generated function hides the fetch, serialization and error handling; React just
sees a normal function.
Write the action in an actions.rs file anywhere under src/routes/:
...then call it from React - fully typed, input and output inferred from your Rust structs:
The same generated function works three ways: as the imperative RPC above, as a
<Form> with progressive enhancement (it still submits with JavaScript disabled,
via a 303 redirect), and passed straight to useActionState for stateful forms -
so isPending and useFormStatus() come for free. Expected failures return
Err(ActionError) and surface as a thrown OssidoActionError or in your form
state; panics are caught and shown in the dev overlay.
File uploads are supported too: add a Files parameter and the client
automatically sends multipart/form-data when the form carries files.
See Server Actions for the full guide, including stateful actions, error handling and file uploads.
Ossido renders React on a V8 isolate on the server. Until now that isolate only ran
synchronous microtasks - so anything that resolves asynchronously during render
never completed. React 19's streaming renderer resumes suspended work through
MessageChannel macrotasks, and the isolate never executed them. In practice that
meant Suspense boundaries, use(promise), React.lazy and top-level await could
hang or hydrate mismatched, and static exports came out stuck on "Loading..."
placeholders instead of finished HTML.
0.1.3 gives the isolate a genuine event loop:
setTimeout, setImmediate, clearTimeout, clearImmediate -
backed by a per-isolate queue.MessageChannel matching browser semantics, so React's Fizz
renderer can resume suspended work.The upshot: Suspense and await resolve during rendering, hydration mismatches
from render-time async go away, and static generation produces complete,
fully resolved HTML.
Fully synchronous pages are unchanged and pay zero overhead. And because the build
clock is virtual, work that can never resolve at build time - a live fetch to a
running server, say - fails gracefully rather than hanging. (That virtual clock is
a deliberate trade: it favours build speed over real-time delays, so it isn't for
work that needs actual wall-clock timing during a build.) More in
SSR, SSG & Streaming.
Bump the ossido crate and the @ossido-labs/ossido package to 0.1.3, and
update the CLI:
The full diff is in the 0.1.3 release notes (0.1.2...0.1.3).