---
title: Components
---

# Components

- **`<Defer>`**: defers content into a placeholder. It renders its own
  placeholder, so it is self-sufficient
- **`<Slot>`**: a named placeholder, filled by a `<Defer>` that renders
  elsewhere in the tree

## `<Defer>`

Registers its `children` as deferred content and renders a placeholder in its
place, showing `fallback` (empty when omitted) until the content is ready. Its
id is generated, unless `target` names a `<Slot>` (below).

```tsx
import { Defer } from "@vincle/flow";

<Defer fallback={<p>Loading dashboard…</p>}>
  <HeavyDashboard />
</Defer>;
```

Pass a factory to receive an `AbortSignal`. It fires when the client
disconnects or the fragment's `timeout` elapses; forward it to your own
cancelable work (e.g. `fetch(url, { signal })`) so that work stops early:

```tsx
<Defer fallback={<p>Loading dashboard…</p>}>{(signal) => <HeavyDashboard signal={signal} />}</Defer>
```

The signal does not cut the render short: if it has fired by the time the render
finishes, the result is discarded and routed to `onError` instead of being
emitted.

### Merge types

`merge` controls how the content applies to the target element:

| `merge`     | Effect                                    |
| ----------- | ----------------------------------------- |
| `"replace"` | Target element is replaced (default)      |
| `"append"`  | Content inserted as last child of target  |
| `"prepend"` | Content inserted as first child of target |
| `"before"`  | Content inserted as previous sibling      |
| `"after"`   | Content inserted as next sibling          |
| `"morph"`   | Target is diffed against content in place |

`"morph"` keeps focus, scroll position and form state. Not every adapter
supports every merge type: an unsupported one is rejected at registration with a
clear error.

## `<Slot>`: named insertion point

A named placeholder for content deferred **elsewhere** in the tree: typically a
layout reserving a spot, filled by a page component that cannot reach into that
layout. Its `children` render immediately as placeholder content, until the
matching `<Defer>` replaces them. `name` is required.

When placeholder and content live in the same spot, skip `Slot`: `Defer` with
its own `fallback` does the job.

The `<Slot>` is the placeholder: its `<Defer>` emits none of its own, wherever
the two sit in the page.

```tsx
import { Defer, Slot } from "@vincle/flow";
import { Results } from "./results";

function SearchPage() {
  return (
    <>
      <main>
        <h1>Search results</h1>
        <Slot name="results">
          <p>Loading results…</p>
        </Slot>
      </main>
      <Defer target="results">
        <Results />
      </Defer>
    </>
  );
}
```

## Content types

`Defer` children (the `DeferContent` type) are a value, or a factory receiving
the `AbortSignal` and returning one:

| Content                      | Behaviour                                 |
| ---------------------------- | ----------------------------------------- |
| `JSX.Element` or `string`    | One patch, rendered once                  |
| `AsyncIterable<JSX.Element>` | A stream: one patch per item, as it comes |

The runtime consumes an `AsyncIterable` itself: no `.then()` or `for await` on
your side. See [Streaming](/integration/streaming#async-iterables-streaming-sequences).

## Props

| Prop       | Applies to | Meaning                                                       |
| ---------- | ---------- | ------------------------------------------------------------- |
| `name`     | `Slot`     | id of the placeholder (required)                              |
| `target`   | `Defer`    | optional placeholder id; generated automatically when omitted |
| `fallback` | `Defer`    | placeholder content shown in the shell                        |
| `merge`    | `Defer`    | how content applies to its target (default `"replace"`)       |
| `timeout`  | `Defer`    | per-fragment render timeout in ms                             |
| `onError`  | `Defer`    | per-fragment error handler, overriding the renderer's one     |
