---
title: Slots
description: A slot is a place in an actor that other actors are spawned into, in an order the scene controls.
mode: wide
---

A slot is a field that says where children of an actor can go. It holds nothing when the
actor spawns. Whoever uses the actor fills it: the scene, the actor's own `onInit`, or one of
its commands. Use a slot when the actor is a container whose contents vary, such as a window,
a list or a deck. For a part that every instance has, declare a [child](/docs/runtime/children)
instead.

```ts twoslash title="word.ts"
import { derived, LayoutActor, signal, slot } from "@motionactor/core";

class Letter extends LayoutActor {
  char = signal("");
}

export class Word extends LayoutActor {
  text = signal("");
  letters = slot({ accepts: Letter, multiple: true });

  spelled = derived(() =>
    this.letters.all().map((letter) => letter.char()).join(""),
  );

  onInit() {
    for (const char of this.text()) {
      this.letters.spawn({ char });
    }
  }
}
```

## Declaring

`slot({ accepts, multiple })` takes the class it accepts, or an array of classes. Spawning
anything else into it throws an error naming the slot and the class. A subclass of an accepted class is accepted, so `slot({ accepts: LayoutActor })` takes any
actor that has a box.

`multiple` is `false` by default, and the slot holds one actor at a time.

## The handle

When the actor spawns, the field becomes a handle on the slot.

| Handle | One | Multiple |
| --- | --- | --- |
| `spawn(Class, overrides)` | Spawns into the slot; throws while it is occupied | Spawns at the end |
| `spawn(overrides)` | The same, constructing the accepted class | The same |
| `current` | The occupant, or `undefined` | |
| `all()` | | The occupants in order |
| `reorder(next)` | | Sets the order |

`spawn(overrides)` needs one accepted class to construct, so it throws when `accepts` is an
array or is `Actor` or `LayoutActor` itself. Pass the class then.

Reads go through the timeline. `all()` and `current` in a scene script give the occupants at
the cursor; in a `derived` they give the occupants at the frame being resolved, so `spelled`
above follows every change to the slot:

```ts twoslash
// @filename: word.ts
import { derived, LayoutActor, signal, slot } from "@motionactor/core";
class Letter extends LayoutActor {
  char = signal("");
}
export class Word extends LayoutActor {
  text = signal("");
  letters = slot({ accepts: Letter, multiple: true });
  spelled = derived(() => this.letters.all().map((letter) => letter.char()).join(""));
  onInit() {
    for (const char of this.text()) {
      this.letters.spawn({ char });
    }
  }
}
// @filename: word.scene.ts
// ---cut---
import { createScene } from "@motionactor/core";
import { Word } from "./word";

const compiled = createScene(function* (ctx) {
  const word = ctx.spawn(Word, { text: "stop" });
  yield* ctx.wait(1);

  const [s, t, o, p] = word.letters.all();
  word.letters.reorder([p, o, t, s]);
  yield* ctx.wait(1);

  yield* t.exit();
  yield* ctx.wait(1);
  return { word };
}).build({ viewport: { width: 1920, height: 1080 }, fps: 30 });

compiled.at(0).scene.word?.spelled; // "stop"
compiled.at(30).scene.word?.spelled; // "pots"
compiled.at(60).scene.word?.spelled; // "pos"
```

`reorder` takes every current occupant once. Leaving one out, adding one or repeating one
throws.

## Leaving a slot

An occupant leaves when it exits. `exit()` runs its [`onExit`](/docs/actors/actor#lifecycle) and
then despawns it, and the slot is free from that frame on. A one-actor slot can then take a
new occupant.

<DeckSlotsScene />

A deck with one multiple slot. The deck's `deal` and `collect` stagger the cards'
`enter()` and `exit()` through `this.cards.all()`, never touching a card's state. The
`reorder` between them changes the order `all()` returns, so `collect` takes the cards in
their new order.

## Drawing a slot

Each resolved actor lists its occupants' ids by slot in `slotChildren`, in order. Each
occupant has its host's id in `parentId` and the slot's name in `slotName`. A
[renderer](/docs/rendering/renderers#slots) receives the occupants already drawn, as `slots`,
and places each element where the slot belongs.

Because a host only names the class it accepts, it can hold actors it knows nothing else
about. The [Desktop example](/docs/examples/desktop) has windows with
`slot({ accepts: LayoutActor })`, and each window hosts an app it never imports.
