Data Binding in Svelte
Over the last few weeks, I have been getting familiar with Svelte and SvelteKit in particular. Coming from a React background there are many similarities using Svelte and some that are not.
One thing that came up when using Svelte was passing values from a child to parent component and how that worked. Iām not going to go into detail on Svelte in particular, more some patterns to use.
As with many things in programming, there are several ways to do any one thing, in this post Iāll go over some of the approaches I have used and when to use them.
Iāve created examples of these in the Svelte REPL so you can have a play around with them to familiarise yourself with them. Iād also recommend checking out the Svelte documentation; itās an awesome source of information.
First up, it might be important to talk about props (short for properties) and how they are passed between components.
Passing props down to a child
So hereās a super simple App.svelte (parent) that is importing the
child component and passing a value, (in this case) propValue down
to a <Child /> component:
<!-- App.svelte -->
<script>
import Child from './Child.svelte'
</script>
<Child propValue="Pass this to the child!" /> Much the same way as you would want to define a variable in React if the variable is scoped to the component then itās defined there.
Svelte is a superset of HTML with the .svelte extension so whereas
in React a prop would be brought in as a parameter in the case of
Svelte itās defined in script tags and exported. This means that the
variable can be wrapped in some curly boys {} in the markup (HTML)
so it can be interpreted and read out by JavaScript.
<!-- Child.svelte -->
<script>
export let propValue
</script>
<p>I'm taking this from the parent: {propValue}</p> But whatās this export? I struggled with this initially, ultimately
itās a way to make propValue available to the parent. If you think
thatās weird, just wait until I detail reactive declarations! More on
that soon.
Passing props back to a parent
As a general rule data flow goes from the parent to the child but what if you want to pass a value back from the child to the parent?
Using bind:value
In Svelte using the bind:value directive (command) to, aāhem bind
the value of the text input to a variable value, this is the
shortest example:
<!-- Input.svelte -->
<script>
let value = ''
</script>
<input bind:value />
<p>{value}!</p> Although value isnāt a great variable name so it can be changed
further:
<!-- Input.svelte -->
<script>
let descriptiveVariableName = ''
</script>
<input bind:value="{descriptiveVariableName}" />
<p>{descriptiveVariableName}</p> Thereās a drawback with doing it this way which Iāll come onto in a
bit. For now, I have the data bound to that input, and changing the
text will update the p tag with what is added to the text input.
Ok, now Iām treating that input as its own component so, say I want to
access the input value from a parent? āHow to pass the bound value
back up to the parent?ā Iām going to go back to the previous example
with the less descriptive variable name now and remove the p tag as
I want to display the value in the parent, Iām also going to rename it
from Input.svelte to Child.svelte:
<!-- Child.svelte -->
<script>
export let value = ''
</script>
<input bind:value /> I can now access the bound value of Child.svelte by defining a
variable in the parent (App.svelte) component, inputValue in this
case and pass that to the child:
<!-- App.svelte -->
<script>
import Child from './Child.svelte'
let inputValue = ''
</script>
<Child bind:value="{inputValue}" />
<p>Input value is: {inputValue}</p> Iām adding inputValue to a p tag on the parent now to get that
value. Changing the input in the parent now updates the inputValue wrapped in the p tag in the parent.
So Iām updating the Child component on the parent and getting that
value back in the inputValue variable.
Now, Iām going to go back to the input example with the descriptive variable and try the same:
<!-- Child.svelte -->
<script>
export let descriptiveVariableName = ''
</script>
<input bind:value="{descriptiveVariableName}" /> Now changing the value in the parent doesnāt seem to trigger any
changes, but if I add a <p> tag to the child and make some changes I
can see that the changes are going from the parent to the child but
arenāt coming back to the parent:
<!-- Child.svelte -->
<script>
export let descriptiveVariableName = ''
</script>
<input bind:value="{descriptiveVariableName}" />
<p>Child received props: {descriptiveVariableName}</p> In summary, you can pass down named props to components but if you
want to pass the props back up to the parent then youāll need to use bind:value alone. Something to keep in mind when taking this
approach.
Using a callback
This approach will be familiar if you are used to doing this in React
with adding a callback function. Here an onChange is defined in the Child for the parent to use:
<!-- Child.svelte -->
<script>
export let onChange
let value = ''
$: onChange(value)
</script>
<input type="text" bind:value /> Wait! Whatās that $ doing there? Thatās a reactive declaration which I touched on earlier but didnāt give any explanation. This is
how Svelte can keep track of a componentās state change, so whenever value is changed it updates onChange.
In the parent I can use the onChange from the child to update the
parent:
<!-- App.svelte -->
<script>
import Child from './Child.svelte'
let inputBoxValue = ''
</script>
<Child onChange={newValue => inputBoxValue = newValue} />
<p>Input box value is: {inputBoxValue}</p> This doesnāt have to be bound to the value of an input though; another
way to achieve this could be to do some validation on the input on:blur so that when the user comes out of the input some validation
can happen.
<!-- Child.svelte -->
<script>
export let value = ''
export let onBlur
</script>
<input bind:value on:blur="{onBlur}" /> So as mentioned previously, with onBlur (or whatever you want to
call this function) this can trigger a function in the parent. Iāve
added a parentValidation function to be triggered in this example:
<!-- App.svelte -->
<script>
import Child from './Child.svelte'
let inputBoxValue = ''
const parentValidation = () => {
// validation here
alert(inputBoxValue)
}
</script>
<Child bind:value="{inputBoxValue}" onBlur="{parentValidation}" />
<p>Input box value is: {inputBoxValue}</p> Event forwarding / dispatching an action
Last up is the event forwarding in Svelte because Svelte doesnāt use a virtual DOM like Vue and React component events donāt bubble.
In this instance, Iām using the createEventDispatcher from Svelte to
create a dispatch function for use in the child component and giving
it the label child-blur and passing the input value back with the
dispatcher.
<!-- Child.svelte -->
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
export let value = ''
</script>
<input bind:value on:blur={dispatch("child-blur", value)} /> In the parent much like with doing it with a callback but now in place
of the onBlur callback Iām using the on:child-blur event to
trigger the parentValidation:
<!-- App.svelte -->
<script>
import Child from './Child.svelte'
let inputBoxValue = ''
const parentValidation = ({ detail }) => {
// validation here
alert(detail)
}
</script>
<Child
bind:value="{inputBoxValue}"
on:child-blur="{parentValidation}"
/>
<p>Input box value is: {inputBoxValue}</p> Iād say as a project grows then this method will be the goto with a little boilerplate and a lot of flexibility.
Conclusion
As I said at the beginning, there are a few ways to do this, and depending on your use case there may be a need to use any of these approaches.
I hope you found it useful and that I helped identify some of the bumps you may come across when doing this for yourself.
Resources
Iāve added all the example code here to the Svelte.dev so you can play around with them if youāre so inclined:
There's a reactions leaderboard you can check out too.
Sign up for the newsletter
Want to keep up to date with what I'm working on?
Join other developers and sign up for the newsletter.
I care about the protection of your data. Read the Privacy Policy for more info.