Breaking changes in bold.
You can now sponsor Laminar development!
For versions v0.11.0 and up, see laminar.dev/blog
- New: Airstream v0.10.1, see Airstream changelog
- New: Easily toggle composite attr values
- e.g.
cls.toggle("class1") := true
andcls.toggle("class1") <-- $bool
- e.g.
- New: Easily remove composite attr values:
cls.remove("class1")
- New: Another
cls.set
method with varargs (same for other composite attrs) - New: Add pointer events to Laminar (thanks, @doofin!)
- API: Make
separator
field onCompositeAttr
public
News:
- I published a comprehensive video introduction to Laminar, check it out: Laminar – Smooth UI Development with Scala.js
- Iurii released initial version of tulz-app/laminar-router, an alternative Laminar router with API inspired by Akka HTTP.
- Iurii also shared a template for server side rendering any frontend apps (including Laminar) using Puppetteer: yurique/spa-ssr-proxy
- Anton is working on a static documentation website for Laminar (#61, #63)
- @uosis started work on auto-generating Laminar facades for Web Components: uosis/laminar-web-components. We also have a complete example of Web Components in laminar-examples.
—
Laminar & Airstream development is sponsored by people like you.
GOLD sponsors supporting this release: ✨ Iurii Malchenko
Thank you for supporting me! ❤️
- New: Support custom CSS props (for Web Components)
- No change to Laminar API, but we're using a different method of setting and unsetting all CSS props under the hood.
- New: Upgrade Scala DOM Types to v0.10.1
- Adds
slot
attribute (for Web Components)
- Adds
- New:
eventProp --> var
alias foreventProp --> var.writer
- Build: Upgrade Airstream to v0.10.0: changelog
- New: Airstream v0.9.2, fixes throttle and adds a few convenience methods, see Airstream changelog.
- New:
onMountUnmountCallbackWithState
which is likeonMountUnmountCallback
but can remember user-defined state and provide it to the unmount callback. - New:
observable --> var
alias forobservable --> var.writer
- New:
FormElement
type alias
- Fix: Remove one of the conflicting
amend
methods (#54)- This change should be almost always source-compatible so no changes required.
- New:
nbsp
character now available for convenience - New:
emptyMod
now available (a universal Modifier that does nothing)
News:
- Check out vic/laminar_cycle, a small library that lets you build Laminar applications using Cycle.js dialogue abstraction.
- Build: Upgrade Scala DOM Types to v0.10.0: changelog
- A few naming changes to attributes and tags
- This also bumps scala-js-dom to v1.0.0
- Build: Upgrade Airstream to v0.9.0: changelog
- API:
ReactiveElement.amend()
returnsthis
now - Fix: Incorrect SVG composite attributes types
News:
- Check out yurique/scala-js-laminar-starter.g8, a new giter8 template that sets up a full stack application with Laminar on the frontend, and uses a plain webpack config instead of scalajs-bundler.
This release is a significant improvement to both usability and safety of Laminar. We overhauled the lifecycle event system, and reworked how Laminar makes use of Airstream ownership, fixing longstanding design flaws in the process. We also simplified many things e.g. we eliminated the whole Scala DOM Builder
layer. Below is a comprehensive list of changes with migration tips sprinkled throughout.
- Addons
- New: Waypoint – efficient URL router for Laminar
- Documentation
- The entire documentation was of course updated for v0.8.0. For those already familiar with Laminar v0.7 API, the following sections contain new or significantly updated conceptual material:
- Manual Application
- Reusing Elements
- Efficiency
- Binding Observables
- Ownership
- Memory Management
- Element Lifecycle Hooks
- See also the Changelog for Airstream v0.8.0, and the new Dynamic Ownership section in Airstream docs.
- New blog post expanding on the rationale behind Laminar: My Four Year Quest For Perfect Scala.js UI Development
- The entire documentation was of course updated for v0.8.0. For those already familiar with Laminar v0.7 API, the following sections contain new or significantly updated conceptual material:
- New: Ownership & Lifecycle Events overhaul
- This is the flagship feature of this release.
- Ownership
- Laminar now uses Airstream's new
DynamicOwner
andDynamicSubscription
features:ReactiveElement
subscriptions are now activated every time the element is mounted, not when it is first created. This solves a long standing design flaw (#33). - You can now re-mount previously unmounted elements: their subscriptions will be re-created and will work again.
- Laminar now uses Airstream's new
- Lifecycle Event system
- Eliminate
mountEvents
andparentChangeEvents
streams,maybeParentSignal
signal,MountEvent
type, and all related machinery - New:
onMountCallback
/onMountBind
/onMountInsert
/onUnmountCallback
/onMountUnmountCallback
modifiers - Mount events are now propagated differently down the tree, which has minor timing differences:
- Order of unmount events changed between child / parent components. If a subtree is being unmounted, the unmount hooks on children will fire before they fire on their parents.
- Previously mount events had to wait for the current Airstream Transaction to finish when propagating to child nodes (similar to how EventBus always emits events in a new Transaction). This is not the case anymore. In practical terms this is unlikely to affect you, except that it fixes #47.
- Migration: Read the new docs and choose an appropriate onMount* hook instead of the old streams. Don't rush to use
onMountCallback
, check out all of the hooks first.
- Eliminate
- API: Merge
EventPropEmitter
andEventPropSetter
intoEventPropBinder
)- Unlike the old
EventPropSetter
,EventPropBinder
adds and removes event listeners on mount, not on instantiation. This should not be a problem as event listeners generally don't fire on detached nodes in the first place. - Migration: Replace usages of
EventPropEmitter
withEventPropBinder
. If you were usingEventPropEmitter
as anEventPropTransformation
, those methods will not be available anymore asEventPropBinder
does not extendEventPropTransformation
, so you will need to rewrite those calls differently, more manually. This usage wasn't explicitly documented, so I hope no one actually runs into this.
- Unlike the old
- API: RootNode no longer automatically mounts itself when the provided
containerElement
is itself unmounted.- This improves ease of integration if you want to e.g. render a Laminar element in a React component.
- Migration: this probably does not affect you. If it does, you'll need to call
rootNode.mount()
when appropriate. See the new "What Is Mounting?" section in docs.
- API: Replace
ReactiveEventProp.config(useCapture: Boolean)
withEventPropTransformation.useCapture
andEventPropTransformation.useBubbleMode
- Migration example:
onClick.config(useCapture = true)
-->onClick.useCapture
- Practically this means that you can set
useCapture
anywhere you have anEventPropTransformation
, not just where you have the originalReactiveEventProp
- Migration example:
- API: New modifier subtypes:
Setter
,Binder
andInserter
(oldSetter
renamed toKeySetter
)- The behaviour is the same except for subscription lifecycle as explained in ownership, but you need to know the distinction between the new types to use the new lifecycle hooks.
- API: Eliminate dependency on Scala DOM Builder
- DOM Builder is capable of supporting different DOM backends and even JVM rendering. We have no plans to use either of these in Laminar, so the indirection required by these abstractions is not pulling its weight.
DomApi
- Remove the old
DomApi
trait and companion object.- Combine
domapi.*Api
traits into a singleDomApi
object - Use the new
DomApi
object directly instead of passing implicitdomapi.*Api
parameters.
- Combine
- Remove the old
- Move
Setter
andEventPropSetter
into Laminar, simplify type signatures and rename toKeySetter
andEventPropBinder
- Merge into relevant Laminar subtypes:
Node
->ReactiveNode
(addRef
type param),Comment
->ReactiveComment
,Text
->TextNode
,ParentNode
->ParentNode
,ChildNode
->ChildNode
,Root
->RootNode
,TagSyntax
->HtmlTag
&SvgTag
- Merge
EventfulNode
trait intoReactiveElement
(split members between the trait and the companion object)- Change type of
maybeEventListeners
to haveList
instead ofmutable.Buffer
, it was never intended to be mutable
- Change type of
- Migration: If you reference any of the affected types directly, you will need to import them from Laminar, or use their corresponding Laminar replacements listed above. Other than that, everything should just work.
- API: Limit access / hide / obscure
- Hide
willSetParent
andsetParent
methods. Use Laminar'sParentNode.appendChild(parent, childNode)
or similar. - Move
appendChild
and other similar methods from ParentNode instance to companion object.- Those low-level methods are still available to you as a user, but generally you should avoid them in favor of a reactive approach (various
<--
and-->
methods).
- Those low-level methods are still available to you as a user, but generally you should avoid them in favor of a reactive approach (various
- ReactiveElement.maybeChildren is not mutable anymore (was never intended to be)
- subscribe* methods renamed and moved to ReactiveElement companion object
- Migration: use the new
observable --> observer
modifier, or the new onMount* hooks, and/or the newelement.amend
method. - Make sure to document
nodeToInserter
- Migration: use the new
- Hide
- API: eliminate auxiliary syntax
myElement <-- child <-- childSignal
- Use the new
amend
method instead:myElement.amend(child <-- childSignal)
- Use the new
- API: Remove
ChildNode.isParentMounted
method. Use a similarChildNode.isNodeMounted
instead. - API: Move
ChildrenCommand
out of the poorly namedcollection
package - API: Rename types:
ReactiveHtmlBuilders
->HtmlBuilders
,ReactiveSvgBuilders
->SvgBuilders
,ReactiveRoot
->RootNode
,ReactiveComment
->CommentNode
,ReactiveText
->TextNode
,ReactiveChildNode
->ChildNode
- New:
ReactiveElement.events(ept: EventPropTransformation)
, works the same asReactiveElement.events(p: ReactiveEventProp)
, returning a stream of events- Example:
div(...).events(onClick.useCapture.preventDefault)
- Useful to combine with the new
observable --> observer
method.
- Example:
- New:
element.amend
method - New:
onMountFocus
modifier - focus an element every time it's mounted - API: New alias for inContext:
forthis
- API:
ReactiveElement
and other node types that take type params now havetype Base
defined on their companion objects containing the most generic version of that type, e.g.ReactiveElement[dom.Element]
forReactiveElement
. - Build: Note that this release is version
0.8.0
, not0.8
as I would have named it before.
- Build: Scala 2.13 support
- Fix: Due to a bug in Scala DOM Builder, it would make mistakes tracking ReactiveElements' children when Laminar was reordering them e.g. using
children <-- X
. This would then result in "cannot readnextSibling
of null" errors and potentially other inconsistencies.- I fixed the issue in Scala DOM Builder v0.9.2, and added another reordering test for this particular scenario in Laminar
- Thanks to @gabrielgiussi for reporting, and nailing the test case!
- New: Use DOM Props to set Reflected Attributes.
- Better performance than setting HTML attributes
- I do not expect a change in behaviour due to the largely symmetrical nature of reflected attributes. At least not when using reflected attributes for writing values. Reading might yield subtle differences as mentioned in the link above.
- This change is also potentially breaking because the types of identifiers like
href
changed fromReactiveHtmlAttr
toReactiveProp
. Unless you reference those types or their non-common ancestors in your code you should be fine.
- API: Return
EventStream
type fordocumentEvents
andwindowEvents
props, not the unnecessarily specificDomEventStream
.- Note: small chance of breakage if you rely on
DomEventStream
type in your code
- Note: small chance of breakage if you rely on
- New:
maybe
method for keys. You can now doattr.maybe(optionOfValue)
instead ofoptionOfValue.map(attr := _)
- New:
emptyNode
to make an emptyNode
(implemented as a comment node) - New:
WriteBus.contracomposeWriter
and other improvements from Airstream v0.7.1 - API: Use
Child
/Children
/ChildrenCommand
types more consistently internally; expose aliases in Laminar.scala - Misc: Move code examples from main into test fixtures to remove them from the JS bundle
- Misc: Bump Scala DOM Types to v0.9.4
- New: Airstream v0.5.1 -> v0.7 –
split
,composeChanges
,flatMap
, etc.- See Laminar docs for using
split
to efficiently render dynamic lists of children
- See Laminar docs for using
- API: Hide
ancestorMountEvents
andthisNodeMountEvents
(#42)- Migration: use
mountEvents
,maybeParentSignal
orparentChangeEvents
instead
- Migration: use
- New: Airstream v0.4 -> v0.5.1 – improved Vars, no more State, etc.
- Big update, see Airstream changelog for details and migration guide
- Fix: Bump Airstream to v0.4.1 to fix NPE in error handling
- New: Airstream v0.4 – now with error handling (see Airstream changelog)
- Build: Drop Scala 2.11 support
- New: window and document event streams now available via
windowEvents
anddocumentEvents
objects - New:
unsafeWindowOwner
that never kills its possessions (careful there, see docs) - API:
api/Laminar
/api/L
object no longer includes event props fromWindowOnlyEventProps
- New: Airstream v0.3 – integration with Futures and other improvements (see Airstream changelog)
- Naming: Match naming changes in Airstream v0.3 (
mapTo
->mapToValue
, newmapTo
method)- Migration note: check every usage of
mapTo
methods, the change appears to be source compatible but the new method accepts its parameter by name
- Migration note: check every usage of
- API: Add currying to
subscribe*
methods onReactiveElement
, rename some of those tosubscribeO
- New: Special handling of
cls
,role
,rel
, and other composite attributes (#22) - Misc: Upgrade to Scala DOM Types and Scala DOM Builder v0.9
- New:
subscribe
and-->
methods can now accept anonNext
function in addition toObserver
- New: Better type inference for arguments of
-->
andsubscribe*
methods - Misc: Move
-->
methods fromReactiveProp
toEventPropTransformation
- Docs: Add Changelog
- Switch to Airstream for reactive layer (previously XStream.js)
- Rework event system and subscription logic and API
- Organize required imports under
api
package - Write more documentation and publish laminar-examples
- Multiple API improvements for a smoother developer experience
Initial release.