API

Components

  • What is a component?

    A component is a self-mounting, reusable UI unit. Every component extends Component, which extends BaseComponent.

  • How do I create a component?

    Constructing a component mounts its root element immediately, but does not render it — you must either register it in a parent via child registration or call render() yourself.

  • How do I register child components?

    Override registerChildren() and match each entry's selector to a data-component mount point in your template:

  • How do I pass data to a component?

    Component has two independent mechanisms for feeding it data — pick based on whether the value drives template rendering, or a form field.

  • How do I generate a template?

    Every component inherits generateTemplate(): Nutin.Template (a string or an html result) from BaseComponent. render() calls it, sanitizes the result and inserts the resulting nodes into the component's element.

  • How do I update a component on state change?

    Dynamic rendering relies on events. You can also call .render() directly.

  • How do I remove empty elements?

    Mark an element data-optional, and it's removed after render if it (or its interpolated value) ends up empty. Only the rendering component's own elements are checked.

  • How do I control HTML sanitization?

    Write templates with the html tag. Every ${} in an html template is HTML-escaped, wherever the value comes from (an input, an HTTP response, localStorage, the server):

Views and routing

  • What is a view?

    A view is the top-level orchestrator for a single route. Component and View are siblings - both extend BaseComponent.

  • How do I create a view?

    A view is a routed page — one per route, mounted and destroyed by the router.

  • How do I register a route?

    Route keys are path patterns — see How do I access route parameters? for the :id/:id? syntax.

  • How do I navigate?

    The path's query string and #hash are kept. The path is always resolved on the current site: a path the app didn't write (e.g. from a ?next= parameter) can't send the browser to another host.

  • How do I access route parameters?

    /posts/:id matches /posts/123 but not /posts. /users/:id? matches both /users and /users/123 — an unmatched optional param is simply absent from the params object, not present with an undefined value.

  • How do I use route guards?

    Guards receive the matched route params (not the raw path or query string), and run in array order, stopping at the first non-true result:

Events

  • What are events in Nutin?

    data-event="click:_handler" attributes in a template — local to one component/view, bound and rebound on every render, and torn down automatically in destroy().

  • How do I handle DOM events?

    eventName is bound via addEventListener to thishandlerMethodName. Listeners are rebound (old ones torn down first) on every render, and fully removed on destroy(). If eventName or handlerMethodName is empty, the attribute is ignored.

  • How do I listen to application events?

    this.listen(event, callback) (available on every component/view) subscribes and automatically unsubscribes it on destroy. Prefer this over calling AppEventBus.subscribe directly, which has no automatic cleanup.

  • How do I emit events?

    emit(event, data) is fire-and-forget and synchronous — it calls every current subscriber immediately, in registration order, and returns void. There's no queuing or async dispatch, so a subscriber added after emit() runs won't see that call. A subscriber that throws is logged with console.error, and the other subscribers still run. See How do I listen to application events? for subscribing.

Lifecycle hooks

Services

  • What is a service?

    A service holds state or logic that needs to stay consistent across the whole app, independent of any single component's or view's lifecycle. Every service extends the abstract Service<T> base class, which is a singleton — Nutin guarantees at most one instance of each service class exists.

  • How do I create a service?

    Service<T> is an abstract singleton base. Calling new ExampleService() directly throws — getInstance() is the only valid way to construct one:

  • How do I use a service?

    Import the exported singleton const, not the class:

  • How do I register service cleanup?

    registerCleanup(fn) is protected — call it from inside your own service (typically the constructor) to queue teardown logic. Override onDestroy() for teardown that needs to be async.

Global layout

  • How do I mount global components?

    Call this once in main.ts, alongside registerPipes()/AppRouter(routes). Each entry's component is constructed and rendered internally, its root element's id is set to the entry's id, and:

  • How do I toggle global components?

    Both look up document.getElementById(id) for each given id — the same id you passed to registerGlobals — and toggle only element.style.display. hideGlobals sets 'none', capturing the element's display value beforehand (its inline style.display, or the computed value if none was set) the first time that id is hidden. revealGlobals restores that captured value by default — pass display to override it with something else instead. Other inline styles are untouched, and a missing id is silently ignored (no throw, no warning).

Pipes

  • How do I use pipes?

    data-pipe="pipeName[:arg1,arg2,...]" pipes an element's value/text through one or more registered pipes, writing the result back — .value for <input>/<textarea>, .textContent for anything else.

  • What pipes are available?

    Registered by registerPipes(), which must be called once at bootstrap (in main.ts, before the first render) — none of these names resolve until then.

  • How do I create pipes?

    AppPipeRegistry is the singleton instance — it's the only public entry point (the underlying registry class isn't exported). Register your own pipes once at bootstrap, typically right after registerPipes() in main.ts, and they're immediately usable via data-pipe="slug".

HTTP Client

  • How do I make HTTP requests?

    AppHttpClient is the framework's default HttpClient singleton, without a base URL: a relative endpoint (/api/users) is resolved against the page's origin, and an absolute one (https://…) is used as it is. Only http: and https: URLs are accepted.