Svelte Components Are Not Resilient
How common Svelte patterns encourage non-resilient components.
The term "resilient components" here is coming from Dan Abramov's article Writing Resilient Components.
The principles he outlines are:
- Don’t stop the data flow.
- Props and state can change, and components should handle those changes whenever they happen.
- Always be ready to render.
- A component shouldn’t break because it’s rendered more or less often.
- No component is a singleton.
- Even if a component is rendered just once, your design will improve if rendering twice doesn’t break it.
- Keep the local state isolated.
- Think about which state is local to a particular UI representation — and don’t hoist that state higher than necessary.
The principle we're discussing in relation to Svelte is "Don't stop the data flow.", otherwise phrased as "Props and state can change, and components should handle those changes whenever they happen.".
Let's start by writing a Svelte component.
We want to write a component that fetches some photos from an API (this is taken from the
onMount example in the Svelte docs).
Now that we have a nice
PhotoGrid component, let's write a component for selecting an album:
PhotoGrid With AlbumSelector [Svelte] (broken):
Beautiful, except that switching albums does not update the
This is not the automatic reactivity we were promised by Svelte.
Why does it not work?
The answer is that
onMount is not reactive, and so our component is not reactive.
onMount does not rerun when our props change, so it does not fetch new data when the album prop is changed.
If we were to write our
PhotoGrid component in React (with hooks) it would look something like this:
PhotoGrid with AlbumSelector [React] (not broken):
This component is almost the same as our Svelte version, and yet it works when we change albums, the photos for the selected album are fetched!
The key difference is that we use
useEffect instead of
Using the dependency array we tell React that the effect (fetching photos) should rerun when the
album prop is changed.
Even if we didn't think of this dependency ourselves
eslint-plugin-react-hooks would tell us about it.
When using React hooks there is no concept of
onMount because the idea of only running some code on mount leads to writing non-resilient components,
components that do one thing when they mount, and then don't take prop changes into account.
Previously with React's class component API we would need to use
componentDidUpdate, React hooks improves upon this, it encourages writing resilient components from the start, and we can also write cleanup logic in the same
To fix our Svelte version you might think we could use
but these lifecycle functions are related to the DOM being updated, not to prop updates.
We only want to rerun our fetching when the album prop is changed.
What is the best way to fix this then? I'm not sure what the best way is, but one way would be to implement
useEffect for Svelte ourselves:
PhotoGrid with AlbumSelector [Svelte] (fixed with useEffect):
With this code we use Svelte's reactivity and a custom store to have our fetch rerun whenever the album prop is changed.
It works, I'm not sure this will work with SSR though, and the code is a bit verbose/convoluted.
IMO Svelte should make something like
useEffect part of the framework so that this could work better and be less verbose.
- Our custom
useEffectis not idiomatic Svelte.
- Idiomatic Svelte uses
onMountwhich results in non-resilient components.
- Idiomatic React (Hooks) uses
useEffectwhich results in resilient components, assuming you listen to the linter.
I do like the direction Svelte is heading but I think this is one area that could be improved.
Tweet about this post and it will show here.