---
title: Sprite
description: "Displays one pooled icon at a position."
---

```tsx
import { Sprite } from 'filedom';

<Sprite
  icon={pieceIcon}
  at={position}
  transition={{ duration: 1, easing: 'easeInOut' }}
  dropTransition={{ duration: 0.15, easing: 'easeOut' }}
  onDragged={handleDrop}
/>
```

Sprite must render inside a [Stage](/docs/reference/components/stage); without one it throws. Keep React keys stable when pieces move.

## Props

| Prop | Type | Default | Description |
| - | - | - | - |
| `icon` | `IconDescriptor` | - | Face to display. |
| `at?` | `StagePoint \| null` | - | Icon center in desktop coordinates. Null or omitted parks the sprite. |
| `transition?` | `PositionTransition` | - | Native position animation for ordinary moves. Omit for immediate placement. |
| `dropTransition?` | `PositionTransition` | - | Animation for the move that follows a user drop, including a rejected drop. Falls back to transition. |
| `zOrder?` | `number` | - | Ordering among overlapping sprites in one stage that set it; higher values are on top. Sprites without it keep creation order. |
| `stackingRegions?` | `readonly StackingRect[]` | - | Opaque rectangles as centered offsets from at, for hollow or padded artwork. |
| `concealed?` | `boolean` | - | Hide a prepared file in place. Position writes still apply before it is revealed. |
| `onDragged?` | `((position: StagePoint) => void)` | - | Receives the dropped position in screen coordinates. Update game state to accept the drop; leave at unchanged to reject it. |
| `static?` | `boolean` | - | Own one file, minted at mount in tree order, instead of leasing an interchangeable pooled one. Excludes onDemand. |
| `onDemand?` | `true` | - | Create this sprite's file when it is shown and delete it when hidden. Excludes static, zOrder, stackingRegions and concealed. |

## Where a sprite's file comes from

Every sprite draws a real desktop file. One prop decides where that file comes from:

| prop | file comes from | for |
| --- | --- | --- |
| *(default)* | any warm file that looks the same | pieces, cards, digits |
| `static` | its own file, created at mount in tree order and kept until unmount | text, chrome, anything whose z among siblings matters |
| `onDemand` | its own file, created at show, deleted at hide | text that must draw over icons the user dragged |

A pooled sprite may be handed any file with the right face. A static or
on-demand sprite keeps its own file. `static` and `onDemand` are mutually exclusive.

Pooled and static files share one lifecycle. The stage creates each file under
a name it has never used, hidden, then unhides it at the park under the cover,
waits for it to warm, and only then moves it into place. `static` changes
ownership: the file belongs to this sprite alone and is minted in the order the
sprites registered, which is the order they are written. Finder stacks icons by
creation time, so a block of static sprites mounted together stacks in written
order. Text blocks depend on this for their overlaps, such as a boxed band or
the bleed between grid cells. Pooled files can't give that guarantee, because
files of one face are created together and handed out in any order.

```tsx
<Sprite icon={tile.icon} at={point} static />
```

Static sprites accept `zOrder`, `stackingRegions` and `concealed` like pooled
ones. They need no `prebake` entry because the stage mints their files at mount.
Changing a static sprite's icon replaces its file.

## On-demand sprites

Pooled and static sprites use files that already exist, so showing one is a
move. Its z was fixed when the file was created, before the user dragged
anything. An on-demand sprite's file is created at its position on every show
and deleted on hide. It is the newest file on the desktop, so it draws on top
of everything, including icons the user just dragged.

```tsx
<Sprite icon={bannerIcon} at={won ? center : null} onDemand />
```

Each show costs roughly 15ms per tile. Hiding and showing again mints a new
name, because a reused name would inherit its old z. Since z comes from
creation, `zOrder`, `stackingRegions` and `concealed` have no meaning here, and
`static` would repeat what `onDemand` already says. The props type rejects all four.

## Transition

```ts
type PositionTransition = {
  duration: number; // seconds; 0 places immediately
  easing?: "linear" | "easeInOut" | "easeOut";
  enabled?: boolean;
};
```

Use `enabled: false` to place immediately. There is no speed option. User drags
are handled separately from programmed moves.
