# How Fre's capture() Function Processes Fibers During Reconciliation

> Discover how Fre's capture() function processes fibers during reconciliation. Learn how it identifies fiber types, executes logic, and updates DOM nodes for efficient rendering.

- Repository: [frejs/fre](https://github.com/frejs/fre)
- Tags: internals
- Published: 2026-03-02

---

**Fre's `capture()` function in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) acts as the step-function of the reconciler, identifying fiber types, executing component logic or updating DOM nodes, and returning the next fiber to traverse until the cooperative scheduler yields.**

The **Fre** library implements a modern, React-like fiber architecture for building user interfaces. At the heart of its **reconciliation** phase lies the **`capture()`** function, which orchestrates how each node in the fiber graph is processed, updated, and traversed during the render cycle.

## What Is the capture() Function?

The **`capture()`** function serves as the primary workhorse inside Fre's reconciliation loop. Defined in [[`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts)](https://github.com/frejs/fre/blob/master/src/reconcile.ts), it receives a single **fiber** node and performs the necessary computations to advance the virtual tree's state. After processing the current fiber, it returns the next fiber to visit—either a child, sibling, or an ancestor's sibling—enabling the depth-first traversal of the component tree without recursion.

## Step-by-Step Fiber Processing

When `capture()` processes a fiber, it follows a strict sequence of type checks and update routines. Each step determines how the reconciler treats the current node.

### 1. Fiber Type Identification

First, `capture()` distinguishes between **function components** and **host elements** by checking the fiber's type property. It sets the `isComp` flag based on whether the type is a function.

```ts
fiber.isComp = isFn(fiber.type)               // src/reconcile.ts#L86‑L88

```

This classification determines which update path the reconciler follows for the remainder of the function.

### 2. Short-Circuiting Memoized Components

If the fiber represents a memoized component whose props have not changed, `capture()` immediately bails out to avoid unnecessary work. It resets the memo flag and jumps to the next sibling, skipping the entire subtree.

```ts
if (isMemo(fiber)) {
  fiber.memo = false
  return sibling(fiber)                       // src/reconcile.ts#L90‑L93
}

```

This optimization prevents redundant re-renders of pure components.

### 3. Executing Function Components

For standard function components, `capture()` invokes **`updateHook()`**, which prepares the hook cursor, creates a fragment node, executes the component function, and reconciles the returned children.

```ts
const isMatchSuspenseOrErrorBoundary = updateHook(fiber)   // src/reconcile.ts#L94‑L96
if (isMatchSuspenseOrErrorBoundary) return isMatchSuspenseOrErrorBoundary

```

If the component throws a Promise, **`suspenseRender()`** creates a **Suspense** boundary; if it throws an error, **`errorBoundaryRender()`** establishes an **ErrorBoundary**. These returns exit the current capture cycle to handle the boundary state.

### 4. Handling Host Elements

When `capture()` encounters a non-component fiber—such as a DOM element—it delegates to **`updateHost()`**. This function creates or updates the real DOM node and then reconciles the fiber's children.

```ts
updateHost(fiber as FiberHost)               // src/reconcile.ts#L98‑L100

```

Host processing bridges the virtual representation with the actual browser DOM.

### 5. Advancing the Traversal

Finally, `capture()` determines the next fiber to process. It returns the **first child** to dive deeper into the tree, or if no child exists, it calls **`sibling()`** to find the next sibling or an ancestor's sibling.

```ts
return fiber.child || sibling(fiber)          // src/reconcile.ts#L101‑L102

```

The `sibling()` function walks up the tree until it locates the next valid node or reaches the root, effectively implementing the depth-first traversal pattern.

## The Cooperative Scheduling Loop

The `capture()` function operates within a controlled loop inside **`reconcile()`**, which respects the browser's scheduling constraints. The surrounding code repeatedly invokes `capture()` until the work is complete or the scheduler signals a yield.

```ts
while (fiber && !shouldYield()) {
  fiber = capture(fiber) as any
}

```

This integration with **`shouldYield()`** from [`src/schedule.ts`](https://github.com/frejs/fre/blob/main/src/schedule.ts) enables Fre to implement **cooperative scheduling**, allowing the renderer to pause and resume work to maintain frame rates and responsiveness.

## Practical Code Examples

### Basic Component Rendering

When you mount a component, `capture()` processes the function component first, then the resulting host elements.

```tsx
import { render } from 'fre'
import { h } from 'fre/h'

function Counter({ start }) {
  const [count, setCount] = useState(start)
  return h('button', { onClick: () => setCount(c => c + 1) }, count)
}

const container = document.getElementById('root')
render(h(Counter, { start: 0 }), container)

```

*The `render()` call initiates reconciliation, triggering `capture()` to process the `Counter` fiber, execute its hook logic, and then traverse the button host element.*

### Manual Component Updates

You can trigger reconciliation manually by updating props and calling `update()`.

```ts
// Assuming rootFiber is the fiber returned from render()
rootFiber.props.children = h(Counter, { start: 10 })
update(rootFiber)

```

*This marks the fiber dirty, causing `capture()` to re-execute the component with the new `start` value and diff the resulting subtree.*

### Suspense Boundary Handling

When a component throws a Promise during execution, `capture()` intercepts it to establish a Suspense boundary.

```tsx
function AsyncComp() {
  const data = fetchData()          // Returns a Promise
  return h('div', null, data)       // Throws the Promise
}

render(
  h(Suspense, { fallback: h('span', null, 'Loading') },
    h(AsyncComp, null)
  ),
  container
)

```

*During `capture()`, when `AsyncComp` throws the Promise, the function calls `suspenseRender()` to swap the primary subtree with the fallback UI until the Promise resolves.*

## Key Source Files Involved

Several modules collaborate to support the `capture()` workflow:

- **[`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts)** – Implements `capture()`, `sibling()`, and the diffing algorithm that drives the reconciliation process.
- **[`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts)** – Defines the `Fiber` interface and `TAG` constants used to distinguish node types during traversal.
- **[`src/schedule.ts`](https://github.com/frejs/fre/blob/main/src/schedule.ts)** – Provides `schedule()` and `shouldYield()` for cooperative scheduling around the `capture()` loop.
- **[`src/commit.ts`](https://github.com/frejs/fre/blob/main/src/commit.ts)** – Performs DOM mutations after `capture()` marks fibers dirty and work completes.
- **[`src/h.ts`](https://github.com/frejs/fre/blob/main/src/h.ts)** – Supplies type-checking utilities like `isMemo`, `isFn`, and JSX element creation helpers.

## Summary

- **`capture()`** in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) is the central step-function that processes each fiber during Fre's reconciliation phase.
- It **identifies fiber types** using `isFn()` to route components through `updateHook()` and host elements through `updateHost()`.
- **Memoized components** are short-circuited early by returning `sibling(fiber)` when props haven't changed.
- The function handles **error boundaries** and **Suspense** by catching thrown Promises or errors during `updateHook()`.
- Traversal advances via `fiber.child` or `sibling()`, enabling depth-first walks while integrated with cooperative scheduling via `shouldYield()`.

## Frequently Asked Questions

### Where is the capture() function defined in Fre?

The `capture()` function is defined in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts). It serves as the core work unit processor during the reconciliation loop, handling type checking, component execution, and traversal logic for the fiber graph.

### How does capture() handle Suspense boundaries?

When a function component throws a Promise during execution inside `updateHook()`, `capture()` catches this and returns the result of `suspenseRender()`. This creates a Suspense boundary that renders a fallback UI until the asynchronous operation completes, effectively pausing the subtree's rendering.

### What happens when capture() encounters a memoized component?

If `isMemo(fiber)` returns true and the props haven't changed, `capture()` resets the memo flag and immediately returns `sibling(fiber)`. This skips the entire subtree reconciliation, preventing unnecessary recomputation of pure components.

### How does capture() decide which fiber to process next?

After processing the current fiber, `capture()` returns `fiber.child` if a child exists to continue depth-first traversal. If no child exists, it calls `sibling(fiber)` to find the next sibling or walk up the tree to find an ancestor's sibling, ensuring complete tree coverage without recursion.