Reactivity, State, and Events
Learning Objectives
- You know of reactive programming and know the term reactivity.
- You know how to define reactive variables in Svelte.
- You know how to create a component that can react to events.
Svelte has a reactivity system that can be used to monitor for changes in data, and to update the user interface to match the data. The reactivity system more broadly relates to reactive programming, which is a programming paradigm that focuses on how changes in data are propagated through the application. In the context of web applications, this typically means reacting to changes in data in terms of, for example, updating shown content.
Reactive variables
Reactive variables are defined in Svelte using the $state()
function. The function is given a value and it returns a variable that Svelte keeps track of. As an example, the following outlines a Svelte component that has a reactive variable.
<script>
let count = $state(0);
</script>
<p>Count: {count}</p>
As the value of the variable count is never updated in the above component, the browser shows the text “Count: 0”.
To demonstrate reactivity, we can use JavaScript’s setInterval
function. The setInterval is a function that can be used to call a function at an interval, where the interval is defined as a number of milliseconds. The function takes two parameters, the first is the function to call, and the second is the number of milliseconds to wait before calling the function again.
The following component uses the setInterval
function to update the count once per second. As the variable count is reactive, whenever the count is incremented, the changes are also reflected in the value shown in the browser.
<script>
let count = $state(0);
setInterval(() => {
count++;
}, 1000);
</script>
<p>Count: {count}</p>
When working with reactive variables, there’s no need to explicitly update the user interface as Svelte’s reactivity system takes care of this.
Reactive variables can be used like any other variables. As an example, in the following, the application shows the text “single digits” when the count is less than 10, and “not single digits” when the count is 10 or more.
<script>
let count = $state(0);
setInterval(() => {
count++;
}, 1000);
</script>
<p>Count: {count} ({count > 9 ? "not single digits" : "single digits"})</p>
Reactive objects
The reactive variable above is a number, but we can make (pretty much) any type of data reactive. Let’s adjust our application so that instead of a number, the variable count
holds a reactive object.
In the following, we have an object called countState
with two properties, count
and note
. At the beginning, the value of the property count is set to 0, and the value of the property note is set to “single digits”. As the object has been given to the $state()
function, the object is reactive.
<script>
let countState = $state({
count: 0,
note: "single digits",
});
</script>
<p>Count: {countState.count}</p>
We can create functionality similar to the earlier by again adding the setInterval
function to increment the value of the count property of the countState object. The following outlines a version where the count is incremented by one every second.
<script>
let countState = $state({
count: 0,
note: "single digits",
});
setInterval(() => {
countState.count++;
}, 1000);
</script>
<p>Count: {countState.count}</p>
The application now shows a number that is incremented by one every second. The main difference to the earlier example is that now we are using an object instead of a number. Furthermore, we are updating a property of the object instead of the object itself.
Let’s further modify the application so that it displays the note and changes the note when the value of count is greater than 9. In the following, the note is shown in parentheses after the count.
<script>
let countState = $state({
count: 0,
note: "single digits",
});
setInterval(() => {
countState.count++;
if (countState.count > 9) {
countState.note = "not single digits";
}
}, 1000);
</script>
<p>Count: {countState.count} ({countState.note})</p>
Now, when we open up the browser, we initially see the count and the note “single digits”. When the count reaches 10, the note changes to “not single digits”, and the count continues increasing.
Updates and methods
Updates to the user interface are triggered whenever the value of a reactive variable changes. For basic JavaScript objects including collections, this holds also for changes from method calls.
To demonstrate this, let’s add a new property to the object countState
that is used to keep track of a list of numbers. The list is initially empty, and we add a new item to the list with the method push
every time the count is incremented. The user interface is updated to show the total number of items in the list.
<script>
let countState = $state({
count: 0,
note: "single digits",
numbers: [],
});
setInterval(() => {
countState.count++;
countState.numbers.push(countState.count);
if (countState.count > 9) {
countState.note = "not single digits";
}
}, 1000);
</script>
<p>Count: {countState.count} ({countState.note})</p>
<p>Total numbers: {countState.numbers.length}</p>
Instead of showing the total numbers in the list, we could also have more complex logic such as displaying the sum of the numbers in the list. The following example uses the reduce method of the list to calculate the sum of the numbers in the list.
<script>
let countState = $state({
count: 0,
note: "single digits",
numbers: [],
});
setInterval(() => {
countState.count++;
countState.numbers.push(countState.count);
if (countState.count > 9) {
countState.note = "not single digits";
}
}, 1000);
</script>
<p>Count: {countState.count} ({countState.note})</p>
<p>Sum: {countState.numbers.reduce((a, b) => a + b, 0)}</p>
Events and event attributes
There are a wide variety of events that can happen in a web application. Clicking a button, typing to an input field, hovering over an element, and scrolling are examples of web application events — a more comprehensive list of event types is available on the MDN Web Docs on events.
Whenever an event happens, the browser creates an event object that contains information about the event and sends the information to an event handler. The event handler is a function which, when called, has access to the event object. The event object contains information about the event, such as the target element, the type of the event, and the key that was pressed. What to do with the information — in the event handler function — is defined by the web application developer.
In Svelte, and in HTML in general, event attributes are added to HTML elements. The event attributes typically first have the word on
, followed by the name of the event, e.g. onclick
. The value of event attribute is the code that should be executed when the event happens.
As an example, the following code outlines a Svelte component that has a reactive variable count
and a button. The button has an onclick
event attribute that calls a function incrementCount
whenever the button is clicked. The function increments the value of the count
variable by one.
<script>
let count = $state(0);
const incrementCount = () => {
count++;
};
</script>
<p>Count: {count}</p>
<button onclick={incrementCount}>Increment</button>
Whenever the button of the above component is clicked, the incrementCount
function is called, and the value of the count
variable is incremented by one. As the variable count
is reactive, the changes are reflected in the browser.
When going over Svelte documentation available on the internet, you might notice that the event attributes have been in the past written with a prefix on
, followed by a colon, and the event. The format has been, e.g. on:click
, on:change
, on:mouseover
, etc.
In Svelte 5, the event attributes are written without the colon such as onclick
, onchange
, and so on.
Each event is associated with an event object that contains information about the event. The following is an adjusted version of the above example. In the following, instead of incrementing the count by one, we first log the event, and then set the count as product of x
and y
coordinates of the event
<script>
let count = $state(0);
const incrementCount = (event) => {
console.log(event);
count = event.x * event.y;
};
</script>
<p>Count: {count}</p>
<button onclick={incrementCount}>Increment</button>
When the button is clicked, the incrementCount
function is called, the event object is logged to the browser console, and the value of the reactive variable count is incremented.
To see what the event object contains, open the browser console. The console can be opened by right-clicking the browser window and selecting “Inspect” or by pressing
F12
orCtrl+Shift+I
.
In a similar way, we could create a component that keeps track of changes to an input field. The following outlines an example with a reactive variable text
and an input field. The input field has an oninput
event attribute that calls a function textInput
whenever the input field value is changed. The function changes the text
variable value to match the value of the input field.
<script>
let text = $state("");
const textInput = (event) => {
text = event.target.value;
};
</script>
<input type="text" oninput={textInput} />
<p>Text: {text}</p>
Svelte comes with input bindings that make it easier to bind input fields to reactive variables. Let’s look at these next.