SolidJS: Purpose of the `keyed` attribute in <Show>
Tags: solidjs, tutorial, webdev
Published: 2024-01-02 14:44:08

If you are like me, you are thoroughly confused by the `keyed` attribute in the `<Show>` control flow component from Solid's core library. Well not anymore since I'll explain everything. Let's talk why you should care and how to use it properly.

I first met with `keyed` when I wanted to use `<Show>` in a more convenient way. So typically one uses Show like this:

```typescript
<Show when={currentUser()}>
  {children}
</Show>
```

And indeed if that's all you need, you don't have to care. The problem is that, if you are using any static type checking like TypeScript, the `currentUser()` part can at least in theory return falsy value. So if you plan to use it inside `<Show>`, you'd have to check for truthiness all the time.

```typescript
<Show when={currentUser()}>
  <>
    <span>{currentUser()?.name}</span>
    <span>{currentUser()?.age}</span>
  </>
</Show>
```

Which is inefficient and tedious, but even worse it's logically unnecessary because you already know inside the `<Show>` that the `currentUser()` exists, otherwise it wouldn't get displayed. But since the type system doesn't have the guarantee that each call to `currentUser()` returns the same value, ie. it can be impure, it has to assume that subsequent calls to `currentUser()` can still return falsy value. And calling it _inside_ `<Show>` JSX tag doesn't change that fact in any way, because `<span>{currentUser()?.name}</span>` is not scoped to `<Show>`, but rather to the whole component.

## Enter the `keyed` attribute

There is actually great solution and that's using so called type narrowing render function, which looks like this:

```typescript
<Show when={currentUser()}>
  {user => <>
    <span>{user().name}</span>
    <span>{user().age}</span>
  </>}
</Show>
```

It narrows the type of `user` to truthy value since it already knows it's truthy by showing Show's children. Perfect!

But wait a second, how's this connected to the `keyed` attribute? Well before Solid 1.7.0, it [wasn't possible](https://github.com/solidjs/solid/blob/v1.6.10/packages/solid/src/render/flow.ts#L86) to use this render function without using `keyed` as well.

```typescript
<Show when={currentUser()} keyed>
  {user => <>
    <span>{user.name}</span>
    ...
```

This, and the fact the docs don't explain `keyed` led to developers thinking this attribute is just about being able to use the render function, but that's wrong! Starting with Solid 1.7.0 the render function can be used also without `keyed`, but what's its purpose then?

## Purpose of `keyed`

If you have experience with React, `keyed` is somewhat similar to [React `key`](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). React uses it to keep track of DOM elements so that it can efficiently reuse them instead of destroying and recreating. 

And so does Solid. If you don't use `keyed`, when you change `currentUser` to someone else, Solid expects the Show's content not to have _radically_ changed and it reuses the already existing component references and their respective DOM nodes instead of destroying and recreating them, but it of course updates the content based on the changed reactive properties.

However if you use `keyed` and the value in `when` attribute has changed, Solid will actually recreate the Show's children from scratch. You can see it in this example:

{% embed https://stackblitz.com/edit/solidjs-templates-v98kxw?ctl=1&embed=1&file=src%2FApp.tsx %}

Every time you click 'Change current user', currentUser's reference changes, which causes Show's children to be recreated and `<UserDetails>` prints 'mounted' to the console. But if you remove `keyed` from Show (don't forget to also rewrite `user()` to just `user` inside Show), it doesn't get mounted more than once.

## Usage of `keyed`

I'd say you don't want to use `keyed` in most cases. First of all you might lose the benefit of type narrowing:

```typescript
<Show when={currentUser()?.id} keyed>
  {id => <>
    <span>{currentUser()!.name}</span>
    ...
```

Notice that `when` now uses the `id`, not just the object reference. That's because in practice the `currentUser()` reference can change even though the user properties used inside `<Show>` can stay the same. As a result, the render function now accepts just the `id` parameter and not the whole user, which is not necessarily very helpful.

Secondly it can lead to unnecessary re-renders of `<Show>`'s content, typically when you use object references in `when` attribute. This can lead to undesired side effects. If your component performs initialization like fetching and it gets created from scratch every time the Show's `when` attribute changes, it can get pretty inefficient.

In summary, use `keyed` only when you want a complete refresh of Show's content.