Skip to content

v1.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 21 Oct 13:33
77bfe82

Nanoviews 1.0 🎉

Nanoviews' first stable release is out, together with Nano Kit 2.0, whose signals it shares. A view is a tree of element calls, attributes first and children second, that builds real DOM once. The signals bound into it update their own nodes in place: no JSX, no virtual DOM, no re-render, in 3.5 to 7 kB minified and brotlied.

import { signal, length } from 'nanoviews/store'
import { form, input, ul, li, p, mount, component$, for_ } from 'nanoviews'

const Todos = component$(() => {
  const $todos = signal(['Release 1.0'])
  const $draft = signal('')

  return (
    form({
      onSubmit(event) {
        event.preventDefault()
        $todos(todos => [...todos, $draft()])
        $draft('')
      }
    })(
      input({
        placeholder: 'What next?',
        value: $draft
      }),
      ul()(
        for_($todos)(
          $todo => li()(
            $todo
          )
        )
      ),
      p()(
        'Total: ', length($todos)
      )
    )
  )
})

mount(Todos, document.querySelector('#app')!)
// <form><input placeholder="What next?"><ul><li>Release 1.0</li></ul><p>Total: 1</p></form>

Start with the documentation.

⚠ BREAKING CHANGES

  • nanoviews: classList returns a plain string when there is no accessor among the parts.
    Its result type says which of the two comes back.

  • nanoviews: createEffectAttribute, value$, checked$, selected$, files$, ref$,
    style$ and autoFocus$ are gone, along with the EffectAttributeValues and
    EffectAttributeTargets augmentations. style takes an object, textarea takes no
    children, and a plain value or checked sets the DOM property. Files are read from the
    onChange event.

    • feat(nanoviews): bind muted on media elements and settle the control bindings

    audio and video are factories of their own: muted is bound through the DOM property,
    two-way with a writable signal. The controls after a review: a signal of a number is no value,
    the shape of a select's value follows multiple, a value is always written outside the focus
    so a default set later cannot replace it, defaultChecked leaves the indeterminate state alone,
    the selection keeps its direction, checked takes the Indeterminate symbol only, and a
    select also follows the text of its options. The key order is documented where it matters.

  • nanoviews: props$ and the Props$ type are no longer exported from nanoviews.
    Destructure the props in the render and pass them on as they came, reading a value with $get
    where an expression needs it.

  • storybook,testing-library: render of @nanoviews/testing-library no longer takes a [component, ...args]

  • nanoviews: a boolean child, or an accessor that returns a boolean, renders nothing
    instead of the text true or false. Turn the value into a string to show it.

  • nanoviews: children passed to a component or a slot whose render has no children
    parameter are a type error.

  • nanoviews: element and block calls return descriptions instead
    of nodes, div()('x') describes the element and div()('x')() builds
    it, and a view is built only under mount, since bindings and
    effect$ need its scope. children$ is removed in favour of
    component$; context and isolate are context$ and isolate$
    and take one child; effect and effectScope are no longer exported
    from nanoviews, use effect$ in components and effect from
    nanoviews/store in stores. kida 2.0.0-prerelease.20260913103613 is
    required.

  • nanoviews: import provide, inject, getContext and run from nanoviews/store
    instead of nanoviews.

  • nanoviews: the classList$ effect attribute is removed.
    Pass the parts to class instead: class: ['button', () => $primary() && 'primary'].

  • the packages require Node.js 24 or newer.

Features

  • agera,intl,kida,nanoviews,query,router,svelte-kit: rename ValueOrAccessor to Signalish (#210) (c42723a)
  • agera,kida,nanoviews: bottom-up deferred effect scopes (#189) (dd3169f)
  • agera,nanoviews: add show_ with pausable effect scopes (#225) (4c2490d)
  • agera: add selector to wake only the keys whose answer changed (#200) (ac6b35f)
  • agera: derive mounted state from the graph instead of counting subscribers (#191) (9c43fce)
  • agera: keep the source cold when observing through a derivation (#199) (ecc0434)
  • intl: pass unique index to rich tag handlers to use as framework key (#187) (5b4008b)
  • intl: support self-closing tags in rich-text formatter (#185) (fa4fcb8)
  • kida,store: move tasks from @nano_kit/store to kida (#220) (2e5c07c)
  • nanoviews: accept an InjectionContext instance in context$ (#42) (c3a07d8)
  • nanoviews: accept read-only accessors in value$, checked$ and selected$ (#32) (e902e6a)
  • nanoviews: add as_ to hand a row through a transform (#207) (2891ec4)
  • nanoviews: add match_ for a cascade of conditions (#219) (1065b20)
  • nanoviews: add polymorphic$ for components that render the target given in as (#76) (3a72092)
  • nanoviews: add props$ to read any prop in accessor form (#212) (3cd1d77)
  • nanoviews: add show_switch_ and show_match_ that keep every case alive (#51) (2898695)
  • nanoviews: add the nanoviews/svg entry point (#21) (a0ecfe9)
  • nanoviews: bind ref, style, autoFocus and the form controls through attributes (#40) (98c2690)
  • nanoviews: expose swap_, the block if_ and switch_ are built on (#203) (9fc639f)
  • nanoviews: hand the tracking key to the row (#217) (34948cd)
  • nanoviews: infer from the render whether a component takes children (#44) (6553633)
  • nanoviews: lazy descriptions, component$ and slots (#22) (68bbe84)
  • nanoviews: let an effect attribute name the element it sits on (#206) (215f157)
  • nanoviews: remove props$ from the main entry (#52) (d7929da), closes #30
  • nanoviews: remove provide, inject, getContext and run from the main entry (#45) (47cca5a)
  • nanoviews: rename $$children, $$slot and $$slots to a trailing $ (#209) (47f794b), references #188
  • nanoviews: rename effect attributes from $$name to name$ (#188) (468a293)
  • nanoviews: rename the truthy and falsy narrowing types (#211) (577c49d)
  • nanoviews: render nothing for true and false children (#43) (fc3b714)
  • nanoviews: support classList$ on SVG elements (#35) (6db45a3), closes #29
  • nanoviews: take class lists in the class attribute (#38) (36fc036)
  • nanoviews: type modern HTML attributes and events (#34) (7df71c2)
  • require Node.js 24 (#47) (617cdd7)
  • router: add the nanoviews integration of @nano_kit/router (#53) (87fb7f6)
  • ssr: add server entrypoint (#164) (eefd560)
  • storybook,storybook-vite,testing-library: publish the packages and rework the story API (#5) (6d72f43)
  • storybook,testing-library: return a composed story unbuilt, like a component instance (#77) (786d0b9)

Bug Fixes

  • agera,kida,nanoviews,query: run user reducers and event handlers untracked (#193) (03c78db)
  • deps: update dependency magic-string to v1 (#175) (fbf537c)
  • deps: update dependency starlight-llms-txt to ^0.11.0 (#85) (4ff267b)
  • deps: update nano_kit packages to 2.0.0 (#81) (0e2da00)
  • deps: update nano_kit packages to 2.0.0-prerelease.20260919114914 (71ed3a4)
  • deps: update nano_kit packages to 2.0.0-prerelease.20260927130800 (#50) (ed214f9)
  • deps: update nano_kit packages to 2.0.0-prerelease.20261008124401 (#71) (1378608)
  • deps: update nano_kit packages to 2.0.0-prerelease.20261014204046 (#73) (3eb3bc3)
  • kida: keep a resolved factory unstarted until the first read (#221) (bc13a53)
  • kida: keep a child write in the caller's context (#198) (0816533)
  • kida: type toAccessor by what it actually returns (#208) (024e812)
  • nanoviews: apply multi-word and custom style properties (#195) (3270183)
  • nanoviews: drop the React-only attribute keys (#19) (58b8ffd)
  • nanoviews: fire every on* prop, not just the bubbling ones (#204) (42e613b)
  • nanoviews: keep the uninspected mark on the rows of a read-only for_ (#46) (f02d052)
  • nanoviews: let false remove a boolean attribute (#18) (7739fcb)
  • nanoviews: let a slot reach children$ and fail loudly when undeclared (#10) (eea8827)
  • nanoviews: show the for_ placeholder when there is no array at all (#205) (6e6568b)
  • nanoviews: type style objects without installing csstype separately (#63) (89c7879)
  • nanoviews: type throw_ as never (#11) (79a85da)
  • nanoviews: type the value of a when_ child inside match_ (#65) (a10b537)
  • nanoviews: unmount a fragment whose children render nothing (#64) (26b05fd)
  • router: preserve statusCode of loadable notFound pages (#166) (40da5a6)
  • storybook: apply the annotations of setProjectAnnotations to composed stories (#66) (8d91e4c)
  • storybook: derive plain args from the Signalish props of a component (#36) (307ece7), closes #28
  • storybook: render a story inside the context$ of its decorator (#72) (53b8f48)

Performance Improvements

  • agera,kida,store: replace morph with the signal constructor protocol (#197) (23b3370)
  • agera,nanoviews: cut allocations and graph work on the binding paths (#196) (3d1c93b)
  • agera: speed up effect runs by splitting warmup and rerun paths (#178) (f550458)
  • benchmark-nanoviews: take the row key and drop record (#218) (66e9ee3)
  • nanoviews: clear a row range with replaceChildren (#216) (5942baf)
  • nanoviews: create the effect attribute registry on first use (#213) (4113217)
  • nanoviews: inline the property and event names in the control effect attributes (#33) (ca9d009)
  • nanoviews: join a class list with no accessors once, without an effect (#41) (d1038cf)
  • nanoviews: wake only the rows whose value changed (#202) (c71cc9c)