Externals
Externals are values your host app provides to the DSL: state,
functions, and components. Inside the DSL they are referenced with a
$ prefix. This is how a page reaches the outside world — the logged-in user,
a formatDate helper, an icon component from your design system.
Register them when you create the runtime:
import { Nu } from '@nuforge/core';
import * as t from '@nuforge/types';
const nu = Nu.create({
externals: {
states: [
t.externalState({ name: 'user', init: { name: 'Ada', plan: 'Pro' } }),
],
functions: (nu) => [
t.externalFunc({ name: 'shout', func: (s) => String(s).toUpperCase() }),
],
components: [
t.externalComponent({
name: 'Badge',
render: Badge, // a React component
props: [t.componentProp({ name: 'label' })],
}),
],
},
});
Using externals in the DSL
| Kind | Declared as | Used in the DSL |
|---|---|---|
| State | t.externalState({ name, init }) | $user, $user.name |
| Function | t.externalFunc({ name, func }) | $shout($user.name) |
| Component | t.externalComponent({ name, render, props }) | <$Badge label={$user.plan} /> |
component App() {} => (
<div>
<h2>{$shout($user.name)}</h2>
<p>{"Current plan:"} <$Badge label={$user.plan} /></p>
</div>
)
External component tags use the
$prefix and resolve to the React component you passed asrender. Only the props you declare inpropsare forwarded.
State is read-only to the DSL
External state is deep-frozen, so expressions can only read it — they can
never mutate host state. Attempting to assign to an external (e.g.
$user.name = "x") is rejected by the evaluator.
Only the host can update external state, and doing so re-renders every view that reads it:
nu.externals.updateStateValue('user', { name: 'Grace', plan: 'Team' });
This is the bridge for pushing live host data (auth, a store, a query result) into a running page: keep an external state in sync with your app state and the page reacts automatically.
External functions
External functions are plain host functions. They receive the evaluated arguments and their return value flows back into the expression:
t.externalFunc({ name: 'formatPrice', func: (n) => `$${Number(n).toFixed(2)}` });
// DSL: <span>{$formatPrice(item.price)}</span>
The factory form functions: (nu) => [...] gives you the Nu instance if a
function needs to read or change the document.
External components
External components let the DSL render real React components from your codebase — design-system primitives, charts, anything. The frame passes the declared props (and children) to your component:
function Badge({ label }: { label?: string }) {
return <span className="badge">{label}</span>;
}
// registered with props: [t.componentProp({ name: 'label' })]
// DSL: <$Badge label={$user.plan} />
See it live (with host controls that update $user) on the
Examples page.
Next steps
- Runtime & Core API —
nu.change, history, frames. - Extensions — host plug-ins with their own state.
- Security Model — why host state is frozen.