@broxium/runtime
v1.10.0
Published
Broxium Web Runtime
Readme
@broxium/runtime
Version: 1.5.3
Developer-facing runtime for Brodox components. Provides components and hooks that work correctly in both server-side rendering (SSR) and browser hydration contexts.
Installation
This package is pre-installed in the Brodox component build environment. You do not install it manually in component code — just import from it directly.
import { BrodoxImage, BrodoxLink, useRouter } from '@broxium/runtime'Components
<BrodoxImage>
Renders an optimised <img> tag. By default, routes the image through the web engine's /api/image proxy (sharp resize + WebP conversion + disk cache). Use direct to bypass the proxy entirely.
// Proxied — resized, converted to WebP, cached on server disk
<BrodoxImage
src="/uploads/hero.jpg"
alt="Hero"
width={1200}
quality={80}
priority
/>
// Direct — browser fetches S3/CDN URL directly, zero server involvement
<BrodoxImage
src="https://cdn.example.com/hero.jpg"
alt="Hero"
width={1200}
direct
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | required | Image URL or local path under UPLOAD_PATH |
| alt | string | required | Alt text |
| width | number | 1920 | Max pixel width; srcset stops here |
| height | number | — | Height hint for layout |
| fill | boolean | false | position:absolute; inset:0; width/height:100%; objectFit:cover |
| priority | boolean | false | loading="eager" for above-the-fold images |
| quality | number | 75 | Sharp quality 1–100 (ignored when direct) |
| sizes | string | — | Standard sizes attribute |
| className | string | — | CSS class |
| style | CSSProperties | — | Inline styles |
| onLoad | () => void | — | Load callback |
| onError | () => void | — | Error callback |
| direct | boolean | false | Bypass /api/image proxy; serve src URL directly |
Image proxy (/api/image) — default mode
Generates a responsive srcset at standard breakpoints: 320w, 640w, 768w, 1024w, 1280w, 1920w (up to width). Server caches processed images at {UPLOAD_PATH}/image-cache/{md5}.webp permanently. Browser caches with max-age=31536000, immutable.
When to use direct
- Source is already on S3, Cloudflare, or another CDN — no benefit from proxying
- You want to avoid adding load to the web engine server
- Image is already the right size and format
Note: direct skips all resizing and format conversion. The browser receives the raw src URL.
<BrodoxLink>
SPA-aware internal navigation link. Renders an <a> tag with data-brodox-link attribute so the shell can intercept the click and perform client-side navigation with a progress bar instead of a full page reload.
<BrodoxLink href="/about-us">About</BrodoxLink>
// With search params — visible in browser URL, sent to server in POST body
<BrodoxLink href="/pricing?period=9&plan=starter">View Pricing</BrodoxLink>
// Replace history entry (no back button)
<BrodoxLink href="/step-2" replace>Next</BrodoxLink>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| href | string | required | Target path (absolute), may include ?search and #hash |
| children | ReactNode | required | Link content |
| replace | boolean | false | history.replaceState instead of pushState |
| scroll | boolean | true | Scroll to top after navigation |
| className | string | — | CSS class |
| style | CSSProperties | — | Inline styles |
| onClick | (e) => void | — | Called before navigation logic |
How navigation works
- Shell intercepts click (capture phase) via
data-brodox-linkattribute pushStateupdates browser URL immediately (search params visible)POST /api/rendersent with body{ path, tid, ...searchParams }- Progress bar animates while waiting
- On success: new HTML injected, page bundle loaded, islands hydrated
- On error or timeout (12s): falls back to
window.location.href(full reload)
External links: Use a plain <a href> tag. The shell only intercepts data-brodox-link elements.
<BrodoxRouter>
Wraps a subtree that needs access to the router context. Provides useRouter() with the current pathname and navigate function.
<BrodoxRouter>
<Nav />
<main>{children}</main>
</BrodoxRouter><Client>
Island boundary — marks a component for client-side hydration. During SSR emits an empty placeholder div + sibling <script type="application/json"> with props. The shell activates the component after page load (hydration mode: load).
export default function App() {
return (
<div>
<h1>Static heading (server rendered)</h1>
<Client>
<Counter initialCount={0} />
</Client>
</div>
)
}Sub-islands created inside a server component with <Client> are looked up via __registry__ in the parent's page bundle.
Important:
<Client>does not accept ahydrationprop. All islands currently hydrate on page load. Thehydrationmodes documented in the web engine architecture (idle,visible,none,only) are reserved for a future release.
Alternative: If the entire component is interactive (uses
useState, event handlers, etc.), use'use client'on the entry file instead of wrapping everything in<Client>. That compiles the whole component as client-only with zero SSR overhead.
<Server>
Semantic marker — transparent passthrough. Wraps server-only content for clarity.
<Server>
<PriceTable prices={serverPrices} />
</Server><BrodoxHead>, <BrodoxFont>
Server-side no-ops in the current implementation. Reserved for future SEO and font injection support.
Hooks
useRouter()
Returns the current router instance.
const { pathname, navigate, back, forward } = useRouter()
// Navigate programmatically
navigate('/about-us?tab=team')
// Navigate without adding to history
navigate('/checkout', { replace: true })| Property | Type | Description |
|---|---|---|
| pathname | string | Current pathname (no search, no hash) |
| params | Record<string, string> | Route params (future) |
| navigate(href, opts?) | function | SPA navigation; same as BrodoxLink |
| back() | function | history.back() |
| forward() | function | history.forward() |
| prefetch(href) | function | No-op (reserved) |
useParams()
Returns route params. Currently returns {} (reserved for future dynamic routes).
How SSR stubs work
When your component is compiled for SSR, @broxium/runtime imports are replaced by the runtimeServerStubPlugin inside @broxium/compiler with inline server-safe implementations. This means:
- No external module resolution needed inside the server bundle
BrodoxLinkrenders<a data-brodox-link>(not an interactive React component)BrodoxImagerenders an<img>pointing to/api/image(orsrcdirectly ifdirect)Clientrenders a placeholder div for the shell to hydrate- Hooks return static defaults
The client bundle (.client.esm.js) uses the real @broxium/runtime from the browser's import map (/static/broxium-runtime.js).
Changelog
| Version | Change |
|---|---|
| 1.5.3 | BrodoxImage: added direct prop to bypass /api/image proxy |
| 1.5.2 | BrodoxLink: added data-brodox-link attribute (required for shell interception) |
| 1.5.1 | BrodoxRouter.navigate(): pushes full URL including search params |
| 1.5.0 | SPA navigation rewrite: POST body, search params in URL |
| 1.4.0 | Initial island hydration directives |
