Fre's Offscreen Mode: How It Powers Suspense in the Fre Framework

Fre's Offscreen mode is a fiber flag that renders subtrees without committing them to the DOM, enabling Suspense to prepare primary content in the background while displaying a fallback UI.

Fre is a lightweight React-like library that implements a concurrent fiber-based reconciler. Its Offscreen mode is the core mechanism that makes Suspense boundaries possible, allowing the framework to prepare expensive UI updates in memory while keeping the fallback visible. This article examines the implementation details in frejs/fre to show exactly how Offscreen mode works and how it integrates with Suspense.

What Is Fre's Offscreen Mode?

Offscreen mode is defined as a binary flag in the MODE enum located in src/type.ts:

// src/type.ts
export const enum MODE {
  OFFSCREEN = 1 << 1   // flag value: 2
}

Every fiber node in Fre carries a mode property that can hold multiple flags. When MODE.OFFSCREEN is set, the fiber and its entire subtree are treated as invisible to the DOM. The commit phase explicitly checks for this flag in src/commit.ts:

// src/commit.ts
export const commit = (fiber?: FiberFinish) => {
  if (!fiber) return
  // Off-screen fibers are skipped during DOM insertion
  if (fiber.mode & MODE.OFFSCREEN) return commitSibling(fiber.sibling)
  // ... normal DOM insertion logic
}

This early return prevents Offscreen fibers from being inserted, updated, or removed from the actual DOM. However, the reconciler still fully processes these fibers—running component logic, executing hooks, and building the fiber tree—just without the final DOM commit.

How Offscreen Mode Powers Suspense

Suspense in Fre is implemented entirely within the reconciler (src/reconcile.ts). When a component inside a Suspense boundary throws a Promise, Fre catches it and invokes suspenseRender. This function creates two distinct child fragments under the Suspense boundary:

  1. Primary fragment: Contains the actual UI that depends on the async data. It is created with mode: MODE.OFFSCREEN, rendering it in memory but keeping it out of the DOM.
  2. Fallback fragment: Contains the loading UI. It is rendered normally and immediately committed to the DOM.

The relevant logic from src/reconcile.ts demonstrates this:

// src/reconcile.ts (excerpt)
const suspenseRender = (fiber, promise) => {
  const boundary = getBoundary(fiber, Suspense)
  
  // Primary UI – rendered off-screen
  const primaryChildFragment = {
    type: null,
    props: { children: primaryChildren },
    mode: MODE.OFFSCREEN,   // hides until promise resolves
    kids: [],
  }
  
  // Fallback UI – rendered immediately
  const fallbackFragment = simpleVnode(boundary.props.fallback)
  fallbackFragment.key = SUSPENSE_FALLBACK_KEY
  
  // Reconcile both fragments under the Suspense boundary
  reconcileChildren(boundary, [primaryChildFragment, fallbackFragment])
}

When the promise resolves, Fre schedules an update on the Suspense boundary. During the subsequent reconciliation, the primary fragment is recreated without the MODE.OFFSCREEN flag. The commit phase then inserts the primary UI into the DOM and removes the fallback, completing the transition.

Step-by-Step Execution Flow

The interaction between Offscreen mode and Suspense follows a precise lifecycle:

Phase Action Offscreen Behavior
Render Component throws a Promise inside Suspense boundary. suspenseRender creates primary fragment with mode: MODE.OFFSCREEN.
Reconcile Both primary (off-screen) and fallback fragments are reconciled. Primary fibers are built in memory; fallback fibers are prepared for DOM insertion.
Commit Commit loop processes the fiber tree. Fibers with mode & MODE.OFFSCREEN are skipped via early return in commit(). Only fallback appears in DOM.
Resolve Promise settles; update(boundary) is scheduled. New reconciliation begins; primary fragment recreated without OFFSCREEN flag.
Commit (final) Second commit phase executes. Primary UI is inserted into DOM; fallback is removed. Offscreen flag is absent, so commit proceeds normally.

Practical Implementation Example

The repository includes a working demonstration in demo/src/suspense.tsx. The essential pattern involves using lazy to create a component that suspends, wrapped in a Suspense boundary:

import { render, lazy, Suspense, h, useState } from '../../src/index'

// Component that "loads" after 1 second
const Lazy = lazy(() => new Promise(resolve =>
  setTimeout(() => resolve({ default: () => <div>Loaded Content</div> }), 1000)
))

export function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>{count}</button>
      
      <Suspense fallback={<div>Loading...</div>}>
        {/* Rendered off-screen until promise resolves */}
        <Lazy />
      </Suspense>
    </div>
  )
}

render(<App />, document.getElementById('app'))

Browser behavior:

  • Immediately: The button and "Loading..." text appear. The Lazy component's fiber tree is constructed in memory with MODE.OFFSCREEN active.
  • After 1 second: "Loading..." disappears and "Loaded Content" appears. The Offscreen flag was cleared during the update, allowing the commit phase to insert the prepared DOM nodes.

Key Source Files

Understanding Fre's Offscreen implementation requires examining these specific locations:

File Relevant Code Purpose
src/type.ts export const enum MODE { OFFSCREEN = 1 << 1 } Defines the bitmask flag that marks fibers as off-screen.
src/reconcile.ts mode: MODE.OFFSCREEN in primaryChildFragment within suspenseRender Assigns the Offscreen flag to the primary content of a Suspense boundary during initial render.
src/commit.ts if (fiber.mode & MODE.OFFSCREEN) return commitSibling(fiber.sibling) Prevents DOM insertion for off-screen fibers during the commit phase.
demo/src/suspense.tsx Example usage of <Suspense> with lazy components Demonstrates the practical effect of Offscreen mode in a running application.

Summary

  • Fre's Offscreen mode is a fiber flag (MODE.OFFSCREEN) defined in src/type.ts that prevents DOM insertion while allowing full component rendering.
  • Commit phase skipping occurs in src/commit.ts, where fibers with the Offscreen flag are bypassed via an early return, leaving the subtree in memory but invisible to the user.
  • Suspense integration leverages Offscreen mode by wrapping the primary async-dependent UI in an Offscreen fragment during the pending state, while rendering the fallback normally.
  • Transition mechanism triggers when the promise resolves: the boundary updates, the primary fragment loses its Offscreen flag, and the commit phase inserts the prepared UI while removing the fallback.

Frequently Asked Questions

What is the difference between Fre's Offscreen mode and CSS visibility?

Fre's Offscreen mode operates at the reconciler level, not the presentation layer. While CSS visibility: hidden or display: none hide elements that are already in the DOM, Fre's MODE.OFFSCREEN prevents the DOM nodes from being inserted entirely during the commit phase. The fiber tree is built and hooks are executed, but no DOM operations occur until the flag is cleared, making it fundamentally a rendering strategy rather than a styling technique.

Does Offscreen mode work with concurrent features in Fre?

Yes, Offscreen mode is designed to work within Fre's concurrent architecture. Because the reconciler can pause and resume work, an Offscreen fiber tree can be prepared in the background across multiple time slices without blocking the main thread. The commit phase is the synchronous barrier where the decision to skip (due to OFFSCREEN) or insert (when cleared) is made atomically, ensuring consistency with Fre's concurrent rendering model.

How does Fre handle cleanup for Offscreen fibers?

Cleanup for Offscreen fibers follows the standard fiber destruction path. When a Suspense boundary updates and the primary content replaces the fallback, the Offscreen flag is removed from the new primary fragment, but the previous fallback fibers are deleted. If an Offscreen fiber tree is discarded entirely (e.g., the Suspense component unmounts), Fre's reconciler walks the fiber tree and executes cleanup effects (like useEffect return functions) even for fibers that were never committed to the DOM, ensuring no memory leaks occur for prepared-but-unshown UI.

Can Offscreen mode be used outside of Suspense boundaries?

While the primary implementation in Fre couples Offscreen mode with Suspense via suspenseRender, the underlying flag system in src/type.ts and the commit phase logic in src/commit.ts are generic. In theory, any fiber could be marked with MODE.OFFSCREEN to prepare UI in the background. However, the current Fre codebase specifically utilizes this mechanism within the Suspense reconciler logic to handle async dependencies, making Suspense the practical and intended use case for Offscreen rendering in this framework.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →