@miurajs/miura
v2.5.12
Published
The main package for the miura framework, bundling all core modules.
Maintainers
Readme
@miurajs/miura
The main package for the miura framework.
This package bundles and exports all the core modules, making it easy to get started with miura.
Included Building Blocks
MiuraElementfor reactive custom elementshtml,css, andtrustedHTML()template utilities- property, state, and computed reactivity
- structural directives, trusted HTML subtrees, and fine-grained bindings
- component-scoped async resources with
$resource() - component-scoped form state with
$form() - lightweight shared state with
$shared() - tree-scoped dependency injection with
$provide()and$inject() - router bridge helpers with
$route(),$routeSelect(), and$routeData() - route-driven async state with
$routeResource() - island hydration helpers with
$islandProps()and$islandResource() - integrated debugger runtime with framework-level dev overlays and component layers
- signals and shared reactive primitives
Direct reads of signal-backed properties in templates can update at the binding level in both JIT and AOT components. Use transformed expressions when you want normal component rerender semantics; use direct reads for hot text, attribute, property, node, and trusted HTML bindings.
Example
import { MiuraElement, html, component } from '@miurajs/miura';
@component({ tag: 'app-user-card' })
class AppUserCard extends MiuraElement {
user = this.$resource(() => fetch('/api/user').then((r) => r.json()));
template() {
return this.user.view({
pending: () => html`<p>Loading...</p>`,
ok: (user) => html`<p>${user.name}</p>`,
error: (error) => html`<p>${String(error)}</p>`
});
}
}When rendering sanitized HTML, prefer Miura's explicit trusted subtree helper
instead of binding .innerHTML:
import { html, trustedHTML } from '@miurajs/miura';
html`
<article>
${trustedHTML(cleanHtml, {
afterRender: (root) => enhanceArticle(root)
})}
</article>
`trustedHTML() does not sanitize. It marks content that your app has already
sanitized or generated itself, and its afterRender hook runs after Miura
mounts the subtree.
@component({ tag: 'app-signup-form' })
class AppSignupForm extends MiuraElement {
form = this.$form({ email: '', acceptedTerms: false });
template() {
const email = this.form.field('email');
return html`
<form @submit=${this.form.handleSubmit(async (values) => {
console.log(values);
})}>
<input &value=${email} @blur=${email.touch}>
<input type="checkbox" &checked=${this.form.field('acceptedTerms')}>
<p>${email.showError ? email.error ?? '' : ''}</p>
</form>
`;
}
}Async validation is also supported through validateAsync, and is automatically respected by submit() / handleSubmit(). Automatic modes are opt-in through validateAsyncOn: 'blur' | 'change'.
Resources can also participate in shared async caching through key, which gives you cache reuse, in-flight dedupe, and explicit invalidation with helpers like resourceKey(...), invalidateResource(...), and invalidateResourceNamespace(...).
They also support staleWhileRevalidate, plus staleTime / cacheTime cache policy control.
Forms also keep submit outcome state through submitError, submitResult, and submitSucceeded, which helps keep success/error UI close to the form primitive instead of in separate component state.
Server-side field validation can also be mapped back into the form with setErrors().
For submit flows, failSubmit() can capture the submit error and field errors together.
view() can render submit-state UI declaratively from the form itself.
Nested field paths like profile.name and profile.meta.featured are supported too.
For lightweight cross-component state, $shared(key, initial) gives multiple components the same signal instance without requiring a full store setup. Use namespaced keys like blog-editor:theme, sharedKey(...), or createSharedNamespace(...) to avoid collisions.
For parent-to-descendant dependencies, use createContextKey(...) with $provide() and $inject() instead of reaching for shared global keys. Context stays tree-scoped, and the nearest provider wins. When descendants should react to changes, provide a signal or another reactive primitive as the context value.
Route-driven resources now bridge cleanly with router loaders too: $routeResource() can derive route-based cache keys automatically and hydrate from route data before revalidating.
$routeData() can also return the full loader data object when you omit the key, and hydrateFromRouteData: true lets a route resource hydrate from that full payload directly.
Islands can do the same on the server/client boundary with $islandProps() and $islandResource(), so server payloads can hydrate directly into component state before optional client revalidation.
When you build on MiuraFramework, the debugger can be enabled centrally from static config.debugger during development. Individual components can then refine their own debug presentation with static debug or @debug(...), for example to rename a layer label or opt out of reporting in a noisy internal helper component. The debugger runtime logger is exported here as debugLogger so it stays distinct from the component decorator.
See @miurajs/miura-element for the component API and docs for the broader framework documentation.
