---
title: Components
---

# Components

Components for deferring content in the HTML shell. Imported from `@vincle/flow`.

## Defer

Renders a placeholder holding `fallback`, and patches it later with its
`children`:

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

async function HeavyDashboard() {
  const data = await fetch("https://api.example.com/dashboard");
  const json = await data.json();
  return <pre>{JSON.stringify(json, null, 2)}</pre>;
}

// fallback is shown in the shell immediately.
<Defer target="dashboard" fallback={<p>Loading dashboard…</p>}>
  <HeavyDashboard />
</Defer>;
```

`children` is plain JSX, a promise, or an `AsyncIterable` (one patch per
item), or a factory `(signal: AbortSignal) => …` returning any of those. The
signal aborts when the request is cancelled or the `timeout` expires.
`target` is optional: without it, Vincle generates the placeholder id.

**Props:**

| Prop       | Type                                                                                                                                                     | Default      | Description                                             |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------- |
| `target`   | `string`                                                                                                                                                 | Generated id | Optional placeholder id; match a `Slot` name to fill it |
| `fallback` | `JSX.Element`                                                                                                                                            | N/A          | Content rendered in the placeholder                     |
| `children` | `JSX.Element \| string \| ((signal: AbortSignal) => JSX.Element) \| AsyncIterable<JSX.Element> \| ((signal: AbortSignal) => AsyncIterable<JSX.Element>)` | N/A          | Content: plain JSX, a factory, or an async iterable     |
| `merge`    | `"replace" \| "append" \| "prepend" \| "before" \| "after" \| "morph"`                                                                                   | `"replace"`  | How content applies to the target                       |
| `timeout`  | `number`                                                                                                                                                 | N/A          | Per-fragment render timeout in ms                       |
| `onError`  | `(error: unknown, info: FlowErrorInfo) => JSX.Element \| void`                                                                                           | N/A          | Per-fragment error handler                              |

## Slot

A placeholder declared apart from its `Defer`: typically a layout reserving a
spot that a page fills later, by matching `target` to `name`. Its children are
shown until the matching `Defer` patches them.

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

async function LiveComments() {
  const res = await fetch("https://api.example.com/comments");
  const comments: Array<{ id: string; text: string }> = await res.json();
  return (
    <ul>
      {comments.map((c) => (
        <li key={c.id}>{c.text}</li>
      ))}
    </ul>
  );
}

function Page() {
  return (
    <html>
      <body>
        <Slot name="comments">
          <p>Loading comments…</p>
        </Slot>

        <Defer target="comments" timeout={5000}>
          <LiveComments />
        </Defer>
      </body>
    </html>
  );
}
```

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

**Props:**

| Prop       | Type     | Default | Description                                        |
| ---------- | -------- | ------- | -------------------------------------------------- |
| `name`     | `string` | N/A     | Fragment id, required                              |
| `children` | `VNode`  | N/A     | Fallback content rendered immediately in the shell |

## Style / Script: asset emission

`<Style>` and `<Script>` emit their tag where they stand, once per `name`: the
first occurrence wins, later ones render nothing.

```tsx
import { Style, Script } from "@vincle/flow/components";

const Page = () => (
  <>
    <Style name="page.css" media="screen">
      {`.hero { color: red; }`}
    </Style>
    <Script name="page.js" defer src="/page.js" />
    <main>…</main>
  </>
);
```

**Style props:**

| Prop       | Type                                  | Default | Description                                       |
| ---------- | ------------------------------------- | ------- | ------------------------------------------------- |
| `name`     | `string`                              | N/A     | Asset id, required, dedup key                     |
| `media`    | `string`                              | N/A     | `media` attribute                                 |
| `children` | `string \| (() => Awaitable<string>)` | N/A     | CSS text, evaluated only if this occurrence emits |

**Script props:**

| Prop       | Type                                  | Default | Description                                        |
| ---------- | ------------------------------------- | ------- | -------------------------------------------------- |
| `name`     | `string`                              | N/A     | Asset id, required, dedup key                      |
| `src`      | `string`                              | N/A     | `src` attribute                                    |
| `module`   | `boolean`                             | `false` | Sets `type="module"`                               |
| `defer`    | `boolean`                             | `false` | Sets the `defer` attribute                         |
| `children` | `string \| (() => Awaitable<string>)` | N/A     | Inline JS, evaluated only if this occurrence emits |

Fragment files written by `emitFragments` carry no `<style>`/`<script>`: the
shell that includes them already has the assets.
