Live today: reactive Flow apps with Signals

Signals enable a new, simpler way to manage UI state in Vaadin Flow. Update a Signal, and the UI updates automatically—no manual listeners or boilerplate.

Join today’s webinar to see how Signals work, what’s available now, and what’s planned next. Includes a live demo and Q&A.

:spiral_calendar: Date: Wednesday, July 9
:clock3: Time: 15:00 CEST / 9:00 AM EDT
:studio_microphone: Presenter: Leif Åstrand
:point_right: Register here

See you today!


The webinar recording has now been published and can be accessed on our Webinars page: Webinars on Full-Stack Java Development | Learn with Vaadin

4 Likes

Thanks for this webinar, quite impressive what is possible already!

Need to dig into Signals soon :slight_smile:

Cheers
Mojo

2 Likes

Is there a technology independent origin for the concept of Signals? There are Signals in Preact, Signals in Angular and now Signals in Hilla and Flow. I guess they are all based on the same ideas, but who “invented” Signals?

There have been similar value holders with automatic listener registrations in some quite early JS frameworks like Knockout and Meteor. A very similar concept is also a central part of MobX.

I would say that the origins of signals like we know them today are from SolidJS. See A Decade of SolidJS - DEV Community for more details.

1 Like

Thank you, @Leif. But does this mean that there is no proper specification and no reference implementation of Signals? Or to phrase the question a bit differently: Does Vaadin use an existing Signal implementation behind the scenes, or did you write your own implementation based on how you interpret the concept of Signals?

It’s our own implementation. We’ve been using the Preact implementation as the reference for our Java implementation to maximize compatibility with Hilla where Preact signals are also used on their own.

1 Like

Are there any support tools for debugging? I can imagine following the flow of where signal updates are coming from etc., can become very complicated in a complex application. Especially when they have multiple layers where one signal depends on others.

2 Likes

There’s no debug tooling yet but we certainly expect to add some once we have run into enough problems to have a better understanding of exactly what kind of assistance would be most useful.

1 Like

Thank you to everyone who attended the webinar yesterday! This is clearly an interesting topic since it’s been a long time since we had this many live attendees in a webinar.

There were many great questions and we had time to answer almost all of them. Many of the questions are such that it might be good to have answers also in textual form in a place where search engines can find them so here we go.

Those “holes”/signals at least conceptually are only for the front-end right?

Signals are mainly intended for managing UI state. But the Java library that we have built is completely framework agnostic so it can also be used for any other case where you want to share state between different parts of a system.

(“holes” refers to the ${something} placeholder syntax that I described with Lit templates as an example. In the Lit implementation, those are referred to as “parts”.)

Where can we find the roadmap of this functionality/other upcoming features?

The roadmap is quite vague since we’re still in discovery mode. Right now, we think that the next thing will be deeper component integration. After that, it’s probably one of form binder integration and router integration but not yet certain which would be first.

We’ll update Releases & Roadmap | Vaadin and Roadmap · GitHub once we have more specific plans.

Creating an effect automatically registers a listener on the used signals. How does removal of these listeners work? Is this through weak references/gc?

The method that creates the effect returns a registration object that you can use to clean up the effect. This is not needed in when using ComponentEffect or upcoming component integrations since those will automatically be tied to the attach and detach events of the owning component instance.

There is no global registration so an effect will also be eligible for garbage collection if all the signals that it depends on are eligible for garbage collection.

The incremental method should be implemented, right?
and
Integer is nice, what about complex objects, even worse, when nested attributes are updated?

I guess this refers to the increment method that I mentioned when talking about atomic operations. We have already defined various signal types with atomic operations for various use cases:

  • ValueSignal with last-write-wins, compare-and-set (replace) and retry-until-no-race (update) operations.
  • NumberSignal with incrementBy(double delta). Note that the delta can be negative for a “decrement” operation.
  • ListSignal with insert and move operations relative to the beginning, end, or any existing item. Child items are represented as ValueSignal which means that you can set/replace/update the value.
  • MapSignal with put and putIfAbsent. The value of each map entry is a ValueSignal to enable additional operations on the value of the entry.
  • NodeSignal that enables nesting lists and maps with access to all operations from the other signal types.

It is not practical to create you own custom signal subclasses to introduce additional operations but you can use the built-in transaction concept to implement basically any other type of atomic operation.

We’re still exploring the pros and cons with different approaches for complex nested objects. There are two basic options: a single ValueSignal<ComplexObject> in combination with Signal::map, or splitting up the object properties into separate ValueSignal / ListSignal / NodeSignal instances and then re-assembling them8 to apply any edits. My gut feeling is that the option with a single ValueSignal in combination with Signal::update to apply edits is easier to deal with and still expressive enough for most cases.

Replacement for Vaadin7 properties? Each cell in form / grid its separate signal?

Yes. History keeps repeating.

We haven’t yet implemented that kind of integration for form/grid but the vision goes in that direction.

In the signal anatomy code example the effect was invoked on the Signal class? How does it relate to the signal created before?

The effect is created using a static method: Signal.effect(() -> {}); since a single effect callback might depend on multiple signal instances. The association happens through running someSignal.value() inside the effect callback. This has the cool consequence that an effect with conditional logic only depends on the signals that were read the last time it was invoked.

This effect will depend on only one out of signal1 and signal2 at any given time. Which of them it depends on varies with the value of booleanSignal.

Signal.effect(() -> {
  if (booleanSignal.value()) {
    System.out.println(signal1.value());
  }  else {
    System.out.println(signal2.value());    
  }
});

How does Signal.effect() understand what the dependencies are? Is it a global execution of all effects, which seems very inefficient so I doubt that.

The signal.value() method that reads the value of a signal uses a ThreadLocal to find out if it’s run from an effect and in that case registers a listener to invalidate the effect if the signal changes. You can use signal.peek() to read the value without registering a registering any listener and Signal.untracked(() -> {}) method for running a larger block without tracking.

Just to be sure, it still requires a com.vaadin.experimental.flowFullstackSignals=true so how mature is the framework in version 24.8.x?

The feature flag is required in Vaadin 24.8. The implementation is production ready in the sense that there are no known bugs that would make the whole thing explode. The reason for the feature flag is that this is a big new concept and we don’t yet have enough experience with it to be 100% certain about the API design. On other words: there’s still a risk that we will have to do some impactful breaking changes even though we don’t have anything specific in mind for now.

I have a type of “shopping cart” app. in short, the order lines are complex, we add 100’s of them, and all of them takes 1-10 seconds. We started using Mono/Flux to handle the backend. being “backend-driven”, we’ve ended up with a lot of UI.getCurrent() … could the signal change within a flux?

All signal types are thread-safe and can be invoked from anywhere. Furthermore, any effect created while a Vaadin session is locked will automatically be run with that same session locked so there’s no need to worry about ui.access when using signals. This means that it’s a good pattern to just directly update the value of a signal from a Flux subscriber.

Depending on the case, it might also be an option to replace some Mono/Flux usage with signals instead if the main purpose is just to keep the UI updated.

how many signals can be placed in memory without any issues?
and
Have you looked at performance in using Signals versus the traditional methods of tracking change?

I don’t have any exact number since we haven’t yet even checked if there’s anything that would need optimizing. There’s certainly some overhead since this is an additional level of abstraction.

The overall architecture should be sound in the sense that resource consumption should be linear rather than e.g. O(n^2) and there are no global locks that might cause contention issues.

Hi, do Signals run purely server side or they have a client side representation as well ?

All the effect variants just end up running a regular server-side setter such as Span::setText and from there on the changes are propagated to the client in the same way as if you’d run that method directly.

We are also working on a client-side representation for use with Hilla but there are no plans for using that with Flow.

Do you mind sharing the code sample of the second demo via github or anything similar?

Both examples shown in the live coding part of the webinar are from this forum thread: Trying out signals in Flow with Vaadin 24.8

How long will it take that we have all business applications in just one line?

I suspect return new BusinessApplication(database); is more about AI and less about signals.

Is there any support for debugging? I imagine following the flow of where signal updates are coming etc. can become very complicated in complex applications…

There’s no debug tooling yet but we certainly expect to add some once we have run into enough problems to have a better understanding of exactly what kind of assistance would be most useful.

Are there any plans / ideas to implement Signals between multiple app-instances? Maybe an API to implement for external tools like a message broker?

The architecture is designed with clustering in mind so that all operations can be asynchronous and the concept has been verified to work in a prototype.

The idea is indeed to have a “bring your own cluster infrastructure” approach to allow integrating with whatever existing tool you already have for coordinating across your cluster. It should be possible to integrate with any tool that can provide strongly ordered persistent event logs, e.g. PostgreSQL (using LISTEN/NOTIFY), Hazelcast, Redis, or Kafka. Just a regular SQL database is not enough without a way of listening for updates and a regular message broker is not enough without a way of retrieving past events but both could be used together to give a working setup.

Is it advisable to use signals to create a tree-type menu, with the structure read from tables defined in a database and filtered according to the role of the user connecting?

That should work but it might be overkill since the menu is typically static after the initial filtering when the user logs in. Signals are mainly for state that can change on-the-fly which might be the case for the menu if the application is built so that you can change the active user without going through a typical logout and login sequence.

How can we implement the signals application wide by using database instead of a static field. For e.g if a table row is updated in database manually and some user is actually viewing the data linked to that row in a page.

You can use SignalFactory.IN_MEMORY_SHARED and a naming convention like tableName/primaryKey, e.g. orders/123.

You also need to ensure the signal is updated when something is changed in the database. If the database is only written through the application itself, then you can just update the right signal from application logic or e.g. JPA lifecycle listeners like @PostPersist. If there are external writers, then you might need to use some database-specific triggers and/or a generic CDC solution like Debezium (or just polling the database for changes if a short delay is acceptable).

There are some ideas to provide built-in support for this but that’s not yet among our top priorities.

How easy is it to test code that depends on Signals. Must there be an active Vaadin UI / Session for these things to work?

Standalone signals can easily be unit tested - you need to set up a SignalEnvironment but there’s nothing specific to Vaadin there. Usage in combination with ComponentEffect or similar requires components to be attached which in turn means that a UI is needed.

2 Likes

This was a worry expressed here as well. The state decoupling is nice, but debugging why ComponentEffect is not firing looks tricky. Where to set the breakpoint ?

One of the nice things about Vaadin today is that it’s rather “procedural” and causality is easy to find. Signals go slightly into the magic direction, not saying this is bad though. As with all patterns, use when appropriate.

Will this get some attention in Testbench to remove whatever boilerplate (if any) would be needed?

Based on my experience so far, it’s relatively uncommon to have bugs in application logic. All the bugs I’ve struggled with so far have been on the framework side but those have also been fixed promptly.

The most common issue might be that you’re accidentally mixing up signal names when using SignalFactory so that you update the value in a different signal than the one that the effect listens to. One thing that could be helpful for that case would be if Signal::toString tells the number of current subscribers since a breakpoint in application code might then help you realize that you’re updating the value of a signal that has 0 listeners.

Another potential issue might be if a computed signal implementation unexpectedly returns a value that is identical to the previous value since that will not trigger any effect update. Any ideas on what would help discover such cases?

Another thing that we have imagined is that Copilot could show a list of all signals that are in use on the current view and also allow observing changes and changing values directly through Copilot. One open question there is whether we can find a name to associate with each signal instance in the UI or if they would have to be anonymous.

Certainly. I created Make sure UI unit tests using signals work out of the box. · Issue #1968 · vaadin/testbench · GitHub to reduce the risk that it’s forgotten.

1 Like

The webinar recording has now been published and can be accessed on our Webinars page: Webinars on Full-Stack Java Development | Learn with Vaadin