Melt UI components are uncontrolled by default, but offer the ability to be controlled.
Controlled components are an optional advanced feature and should only be used when necessary. If you're unsure if you should be using the components in a controlled way, you likely don't need to.
Controlled vs. Uncontrolled
There are various definitions for controlled/uncontrolled components, but in the context of Melt UI, uncontrolled means that the state and stores of a component are created and managed automatically. This is the default behavior, and in most cases, it is more than enough.
In the example above,
open is a store that
createDialog returns. You can read its value to
determine whether the
Dialog is open or not. Since we're not altering the state in any way besides
user interaction, we consider this to be uncontrolled.
Controlled means that the user (you) can create and/or manage the state and/or stores.
Our goal is to provide as much flexibility as possible, so we offer a few ways to give you more control over the state and behavior of the components.
For the following examples, we'll use the
Dialog builder, but the same concepts also apply to
other builders. Be sure to checkout the builder's API reference to see what controlled options are
Modify Writable States
By default, we provide an
open store from the
Dialog builder that will have its state updated
close element is pressed.
You can update the
open store that's returned at any moment, even without expecting an user
A common use case for controlling the state of a component is to sync it with props, or other internal state. You can do it manually...
But it's harder to manage, can be error prone, and with multiple states and options, it can get messy quickly.
We provide a
createSync function that will improve this situation.
createSync takes an object of writable stores and returns an object with
sync functions. The
first argument of the
sync function is the value that the store should be set to, and the
second argument is a setter function that will be called with the new value of the store.
It's still a bit to write, but it's much more manageable. The
sync function will also ignore
updates that are the same as the current value, so you don't have to worry about unnecessary
Bring Your Own Store
If you wanted to define your own
open store so that its state could be shared and updated by other
parts of your app, then we offer a way for you to supply your own.
It's as simple as passing your own
open store to the
Behind the scenes, we're using the custom
open store you passed in instead of creating our own.
Which means your store will be updated as the default
open store normally would, but you'll be
able to create it and share it with other parts of your app before initializing the Dialog.
Also note that we still return an
open store on the
states object. It uses the same custom store
you pass in though, so modifying it will modify your custom store as well.
We can't guarantee that everything will work as expected if you choose to modify certain state stores outside of the builder, so use this feature with caution. These controls are provided to cover those edge cases where you want to do something that the library won't support out of the box.
A change function is called when the value of a
state store would normally change. It receives
an object with
next properties, whose values are the current and next values of the
next value is what the store would have been set to by default if the change function wasn't
curr value is the current value of the store.
Whatever is returned from the change function will be used as the new value of the store.
The snippet below shows an example of how the change function could be used to prevent the
store from being set to
true based on some arbitrary condition.
Of course, this is a contrived example, but it nicely demonstrates the power of this feature.
Melt automatically attaches listeners for and handles events for various elements to provide functionality. However, there may be times where you disagree with a particular way we handle the event, or you just want to add some extra functionality on top of what we provide.
For each event that we listen to and handle, we also provide a way for you to override the default
behavior, or add your own in addition to ours. The custom event that we dispatch is always prefixed
m- and what follows is the name of the original event.
For example, if we're listening to and handling a
click event on a
trigger element, then we'll
m-click event. If it was a
pointerdown event, we'd dispatch an
attributes for the elements are typed to include these custom events, so you'll get type support for
them in your editor.
Here's what the custom events we dispatch looks like:
By marking the custom event as
cancelable, you may use
preventDefault on it, which signals Melt
to immediately stop handling the event, thus preventing the builder's default behavior from
We also pass the original event as a property on the
detail object so that you can access it in
case you'd like to do something else with it.
So if you in fact wanted to override the default behavior of a
click event on a
then you could do something like this:
Overriding the default behavior of a component should be done with caution. We can't guarantee that everything will work as expected if you override certain events, but we provide this feature to cover those edge cases where you want to do something that the library won't support out of the box.
On This Page