# Core Components of the Fre Library: Architecture and Functions Explained

> Explore the core components of the Fre library its architecture and functions Discover how seven modules deliver a React-like API in under 7 KB

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

---

**The Fre library comprises seven tightly-focused modules—JSX element factory, virtual node helpers, reconciliation engine, hook system, cooperative scheduler, DOM operations, and TypeScript definitions—that together provide a React-like API in under 7 KB.**

Fre is a lightweight, React-compatible UI library written in TypeScript, hosted in the `frejs/fre` repository. Understanding the core components of the Fre library reveals how it achieves component-based rendering with hooks, context, and time-sliced updates while maintaining a minimal footprint. Each component is isolated to a specific source file, creating a clear separation between virtual DOM creation, reconciliation, and platform-specific operations.

## JSX Element Factory and Virtual Node Creation

The **element factory** resides in [`src/h.ts`](https://github.com/frejs/fre/blob/main/src/h.ts) and exports the `h` function (also available as `createElement` from [`src/index.ts`](https://github.com/frejs/fre/blob/main/src/index.ts)). This function transforms JSX or manual function calls into virtual nodes (v-nodes) that serve as the immutable description of your UI.

According to the source code at lines 5-20, `h` performs four critical operations: it normalizes props to ensure an object exists even when omitted, flattens nested children arrays while filtering booleans and null values, extracts `key` and `ref` properties for reconciliation, and returns a plain vnode object via `createVnode`. The `createVnode` helper (lines 37-42) constructs the final object with properties for type, props, key, and ref.

Additional utilities in this module support advanced patterns:

- **`createText`** (lines 44-46) generates text-node fibers with the type `#text`
- **`Fragment`** (lines 47-49) returns children unchanged to enable JSX fragments without wrapper elements
- **`memo`** (lines 51-55) marks function components for shallow comparison to prevent unnecessary re-renders
- **`lazy`** (lines 59-78) enables code-splitting by returning a component that throws a Promise during load, triggering the Suspense mechanism

## Reconciliation Engine and Diff Algorithm

The **reconciler** in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) implements Fre's virtual DOM diffing and time-sliced rendering. This module contains the core work loop that determines what changes must be committed to the DOM.

The public entry point `render` (lines 21-30) creates a root fiber and initiates the work loop. When state changes occur, `update` (lines 32-36) marks fibers as dirty and schedules reconciliation. The `reconcile` function (lines 39-44) serves as the work loop itself, repeatedly calling `capture` until the scheduler yields control back to the browser.

Key internal functions include:

- **`capture`** (lines 86-101): The heart of the diff algorithm that distinguishes between component and host fibers, executes component functions, or updates DOM nodes
- **`diff`** (lines 40-108): An O(N) list differencing algorithm that produces action arrays (`INSERT`, `UPDATE`, `REMOVE`, `MOVE`, `REPLACE`) to transform child lists efficiently
- **`commit`** (lines 16-18 and called from `bubble`): The phase where DOM mutations actually occur via `updateElement` and `removeElement`
- **`suspenseRender`** and **`errorBoundaryRender`** (lines 61-84): Handle asynchronous boundaries and error catching by rendering fallback UI when components throw Promises or exceptions

The reconciler operates in **time-sliced mode**, checking `shouldYield()` from the scheduler after each work chunk to maintain responsive UIs during heavy renders.

## Hook System and Context API

Fre provides a complete React-compatible hook API implemented in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts). The system relies on a **cursor** that walks the `hooks.list` array during each component render, enforcing the rules of hooks through call order rather than runtime validation.

Core hook implementations include:

- **`useState`** (lines 25-27): A wrapper around `useReducer` that manages a single value and its setter function
- **`useReducer`** (lines 29-51): Maintains a state tuple `[value, dispatch]` and schedules updates when dispatch is called
- **`useEffect`** and **`useLayout`** (lines 53-58): Register side effects in `hooks.effect` or `hooks.layout` arrays that execute after the render phase completes
- **`useMemo`** (lines 74-84): Caches expensive computations and recomputes only when dependency arrays change
- **`useCallback`** (lines 86-91): A convenience wrapper around `useMemo` that returns memoized function references
- **`useRef`** (lines 93-95): Returns a mutable ref object that persists across renders without triggering updates

For cross-component communication, `createContext` (lines 14-26) builds a provider component that tracks values and notifies subscribers, while `useContext` (lines 29-40) subscribes to the nearest context boundary and forces re-renders when the context value changes.

## Cooperative Task Scheduler

The **scheduler** in [`src/schedule.ts`](https://github.com/frejs/fre/blob/main/src/schedule.ts) implements a cooperative task queue that works in both browser and Node environments. This component ensures that rendering work never blocks the main thread for more than 5 milliseconds.

Key exports include:

- **`schedule(cb)`** (lines 12-15): Enqueues a callback and initiates a transition
- **`startTransition(cb)`** (lines 8-11): Explicitly marks low-priority updates that can be interrupted by higher-priority work
- **`flush()`** (lines 32-46): Processes the task queue until the deadline (threshold of 5 ms) is reached, then yields control
- **`shouldYield()`** (lines 49-51): Compares `performance.now()` against the deadline to determine if the browser needs control
- **`getTime()`** (line 53): A thin wrapper around `performance.now()` for cross-platform timing

This architecture allows Fre to split large component trees into incremental work units, maintaining smooth frame rates even during complex updates.

## DOM Operations and Property Diffing

Platform-specific code is isolated in [`src/dom.ts`](https://github.com/frejs/fre/blob/main/src/dom.ts), which handles the creation and mutation of actual DOM nodes. This separation allows the reconciler to remain agnostic about the rendering target.

The module exports two primary functions:

- **`createElement(fiber)`** (lines 48-56): Instantiates real DOM nodes—whether text nodes, SVG elements, or HTML elements—and applies initial properties
- **`updateElement(dom, aProps, bProps)`** (lines 19-45): Performs granular diffing of attributes, styles, and event listeners between renders

The `updateElement` function uses a joint iterator to compare old and new props simultaneously, handling special cases such as `style` object diffing and `on*` event listener attachment and removal.

## Type Definitions and Public API

All TypeScript contracts are centralized in [`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts), defining interfaces for `Fiber`, `FC` (function components), `Hook` types, `Action` types for the diff algorithm, and enumeration values for `TAG` and `MODE`. These definitions provide compile-time safety for both internal development and library consumers.

The public API surface is curated through [`src/index.ts`](https://github.com/frejs/fre/blob/main/src/index.ts), which re-exports the essential functions from each module: `h`, `render`, `useState`, `useEffect`, `memo`, `lazy`, `Suspense`, `createContext`, and `schedule`. This centralized export pattern ensures that consumers interact with a consistent interface while the internal architecture remains modular.

## Practical Implementation Examples

The following examples demonstrate how the core components work together in real applications.

### State Management and Side Effects

```tsx
import { h, render, useState, useEffect } from 'fre'

function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const id = setInterval(() => setCount(c => c + 1), 1000)
    return () => clearInterval(id)
  }, [])

  return <button onClick={() => setCount(count + 1)}>
    Clicked {count} times
  </button>
}

render(<Counter />, document.getElementById('root')!)

```

This example utilizes the `h` factory for JSX transformation, `render` to bootstrap the reconciler, and hooks from [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) for state and effects.

### Memoization and Code Splitting

```tsx
import { h, memo, lazy, Suspense } from 'fre'

const Heavy = lazy(() => import('./Heavy'))

const Expensive = memo(({ value }) => {
  console.log('render Expensive')
  return <div>{value}</div>
})

function App() {
  return (
    <Suspense fallback={<span>Loading…</span>}>
      <Heavy />
      <Expensive value={42} />
    </Suspense>
  )
}

```

The `memo` helper prevents unnecessary re-renders via shallow comparison (lines 51-55 in [`src/h.ts`](https://github.com/frejs/fre/blob/main/src/h.ts)), while `lazy` throws a Promise that triggers the `suspenseRender` pathway (lines 61-84 in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts)).

### Context API Usage

```tsx
import { h, createContext, useContext } from 'fre'

const ThemeContext = createContext('light')

function ThemedButton() {
  const theme = useContext(ThemeContext)
  return <button className={theme}>I am {theme}</button>
}

function App() {
  return (
    <ThemeContext value="dark">
      <ThemedButton />
    </ThemeContext>
  )
}

```

`createContext` (lines 14-26 in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts)) constructs the provider mechanism, while `useContext` (lines 29-40) handles subscription and update propagation.

### Scheduling Prioritized Updates

```tsx
import { h, render, startTransition } from 'fre'

function List({ items }) {
  return (
    <ul>
      {items.map(i => <li key={i}>{i}</li>)}
    </ul>
  )
}

startTransition(() => {
  render(<List items={heavyArray} />, document.body)
})

```

Calling `startTransition` (lines 8-11 in [`src/schedule.ts`](https://github.com/frejs/fre/blob/main/src/schedule.ts)) pushes heavy rendering work into the cooperative scheduler, ensuring that high-priority user interactions remain responsive.

## Summary

- **JSX Factory ([`src/h.ts`](https://github.com/frejs/fre/blob/main/src/h.ts))**: Transforms JSX into virtual nodes via `h`, supporting fragments, memoization, lazy loading, and error boundaries.
- **Reconciler ([`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts))**: Implements time-sliced virtual DOM diffing with `capture`, `diff`, and `commit` phases, plus Suspense and error boundary handling.
- **Hooks ([`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts))**: Provides React-compatible state and effect management through `useState`, `useReducer`, `useEffect`, `useMemo`, `useRef`, and context APIs using a cursor-based architecture.
- **Scheduler ([`src/schedule.ts`](https://github.com/frejs/fre/blob/main/src/schedule.ts))**: Cooperative task queue with `schedule`, `startTransition`, and `shouldYield` to prevent UI jank via 5ms time slicing.
- **DOM Operations ([`src/dom.ts`](https://github.com/frejs/fre/blob/main/src/dom.ts))**: Abstracts browser APIs through `createElement` and `updateElement` for node creation and property diffing.
- **Type System ([`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts))**: Centralizes TypeScript definitions for fibers, hooks, and component types.
- **Public API ([`src/index.ts`](https://github.com/frejs/fre/blob/main/src/index.ts))**: Curates exports to provide a clean interface to the underlying core components.

## Frequently Asked Questions

### How does Fre's reconciler differ from React's Fiber architecture?

While both use a fiber-based tree structure, Fre's reconciler in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) implements a simpler cooperative scheduling model. The `reconcile` function (lines 39-44) explicitly checks `shouldYield()` from the scheduler after each work unit, whereas React uses a more complex priority-based lane system. Fre's approach prioritizes bundle size over advanced concurrent features, offering time-slicing through a straightforward 5ms deadline check in the `flush` loop.

### What prevents long-running renders from blocking the browser in Fre?

The **cooperative scheduler** in [`src/schedule.ts`](https://github.com/frejs/fre/blob/main/src/schedule.ts) prevents blocking via time-slicing. When `render` or `update` is called, work is pushed to the `schedule` queue (lines 12-15). The `flush` function (lines 32-46) processes tasks until the 5ms `threshold` is reached, at which point `shouldYield()` (lines 49-51) returns true and the loop breaks, yielding control to the browser via `setTimeout` or `MessageChannel` before resuming with `startTransition`.

### How does Fre implement hooks without React's internal dispatcher?

Fre implements hooks using a **cursor-based approach** in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts). Instead of a global dispatcher, each fiber maintains a `hooks.list` array. The `resetCursor` function (called at the start of component renders in [`reconcile.ts`](https://github.com/frejs/fre/blob/main/reconcile.ts)) initializes a cursor index to zero. Each hook call (e.g., `useState` at lines 25-27) reads from or writes to `hooks.list[cursor++]`, enforcing call-order consistency by array index rather than linked list traversal. This achieves React-compatible behavior with minimal code overhead.

### What is the role of the `h` function in Fre's component lifecycle?

The `h` function in [`src/h.ts`](https://github.com/frejs/fre/blob/main/src/h.ts) serves as the **entry point** for all component descriptions. During the render phase, component functions return values created by `h` (or JSX transpiled to `h` calls). These virtual nodes carry `type`, `props`, `key`, and `ref` information that the reconciler's `capture` function (lines 86-101 in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts)) uses to either invoke child components or generate DOM instructions. Without `h` and its associated helpers like `createVnode` (lines 37-42), the fiber tree could not be constructed.