In a complex UI, we often need to hide components—for example, switching between tabs, showing a modal, or collapsing a section. In Svelte, the natural way to conditionally render is with an if block:
{#if showComponent}
<Child />
{/if}
But there’s a catch: when a component is hidden with if, Svelte actually unmounts it from the DOM. That means the component is destroyed and its state is lost. If the user comes back to it, they’re starting fresh.
So how can we keep the state of hidden components? There are two main strategies:
1. Keep the component mounted, just hide it with CSS
If you don’t want a component to lose its state, don’t unmount it. Instead of if, render it all the time and just toggle visibility with CSS.
<script>
let showComponent = $state(true);
</script>
<button on:click={() => showComponent = !showComponent}>
Toggle
</button>
<div style="display: {showComponent ? 'block' : 'none'}">
<Child />
</div>
Now the <Child /> component is always mounted, keeping its state alive, but visually hidden.
The tradeoff: all components remain in memory even if they’re hidden, which can be expensive if you have many heavy components.
2. Lift state up to the parent
If you don’t want all hidden components hanging around in memory, you can store their state in the parent.
That way, when the child is destroyed and recreated, it gets its state restored from the parent.
<!-- Parent.svelte -->
<script>
import Child from "./Child.svelte";
let showComponent = $state(true);
let childValue = $state("Hello"); // state stored in parent
</script>
<button on:click={() => showComponent = !showComponent}>
Toggle
</button>
{#if showComponent}
<Child bind:value={childValue} />
{/if}
<!-- Child.svelte -->
<script>
let {value = $bindable()} = $props();
</script>
<input bind:value />
Now, even though the <Child /> is destroyed when hidden, the user’s input persists because its state is stored in the parent.
Choosing the right approach
- Use CSS hiding when you want fast toggling and can afford to keep all hidden components in memory.
- Lift the state up when you want to conserve memory, at the cost of slightly more wiring.
Both approaches are valid—it depends on the complexity of your UI and performance requirements.