DEV Community

Thomas Collardeau
Thomas Collardeau

Posted on • Edited on

Props up with Svelte

Passing props down (React)

Let's present the paradigm by creating a small Counter component in React first:

export default function Counter() {
  const [count, setCount] = useState(0);
  const incr = () => setCount(c => c + 1);
  return (
    <div>
      <div>{count}</div>
      <button onClick={incr}>+1</button>
    </div>
  );
}

And let's use it an App component:

import './Counter';

function App() {
  return ( 
    <Counter />
  )
}

Now, let us imagine that we need to know if the count is more than 1 in our App component (maybe we need to pluralize a string if that's the case).

Mmh... we don't have access to count from App. We have to lift the state up and pass it back down as a prop to <Counter />. As a result, we also need to pass down the increment function to update said count. Here is an implementation:


// App.js

import Counter from './Counter';

function App() {
  // state is now in the parent
  const [count, setCount] = useState(0);
  const incr = () => setCount(c => c + 1);
  return <Counter count={count} incr={incr} />;
}


// Counter.js

export default function Counter(props) {
  // the child uses props
  const { count, incr } = props;
  return (
    <div>
      <div>{count}</div>
      <button onClick={incr}>+1</button>
    </div>
  );
}

Nice, it works (sandbox). Yet it's slightly odd to me that our Countercomponent has become a shell of itself (it seems). It doesn't do any counting anymore. It wires in a click handler. Maybe we'd have to rename it? But I digress.

Component Bindings (Svelte)

Let's try to handle the same issue in Svelte. Here is our Counter:

<script>
  let count = 0;
  const incr = () => (count = count + 1);
</script>

<div>{count}</div>
<button on:click={incr}>+1</button>

And a parent component, App:

<script>
   import Counter from './Counter.svelte';
</script>

<Counter />

So we're back in the same situation: we want to use count from Counter in the App component. We could lift state up again as before. But this time, we can actually pass props up without having to re-wire any state. We need to:

1/ export the variable in the Counter (child) component:

export let count = 0

2/ bind to the component value in the App (parent) component

<script>
  let count;
</script>
< Counter bind:count />

We declare the variable in the script tag, and we grab it via component bindings (bind:count). So, we have access to the count in the parent component! Let's actually solve the problem in full, using reactive declarations as well.

App.svelte:

<script>
  import Counter from './Counter.svelte';
  let count;
  $: isMoreThan1 = count > 1;
</script>

<Counter bind:count />
<span>is More than 1: {isMoreThan1} </span>

Counter.svelte:

<script>
  export let count = 0;
  const incr = () => (count = count + 1);
</script>

<div>{count}</div>

<button on:click={incr}>
  +1
</button>

View it in the REPL.

I'm interested in your thoughts. What do you think are the repercussion for componentization in Svelte coming from React? Is there a similar mechanism in Vue or Angular?

Give me a follow on Twitter for more explorations with Svelte :)

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.