# How Fre Implements useContext for Context Propagation: A Deep Dive into the Source Code

> Learn how Fre implements useContext for context propagation. Explore source code and discover its subscriber-based notification and fiber tree traversal techniques. Optimize your Fre.js apps today.

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

---

**Fre implements useContext by combining a subscriber-based notification system with fiber tree traversal, where createContext creates a provider component that maintains a Set of subscriber callbacks, and useContext registers the current component as a subscriber while using getBoundary to locate the nearest provider in the fiber tree.**

Fre is a lightweight React alternative that implements a fiber-based reconciliation engine. Its context system demonstrates how modern JavaScript frameworks achieve efficient state propagation without prop drilling. This article examines the exact implementation details found in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) and [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts), revealing how Fre manages context subscriptions and tree traversal.

## The Architecture Behind Fre's Context System

Fre's context implementation relies on two primary hooks: `createContext` for defining providers and `useContext` for consuming values. The system leverages Fre's fiber architecture to track component relationships and manage subscriptions.

### createContext: Building the Provider Component

The `createContext` function in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) (lines 14-25) generates a context provider component that manages value storage and subscriber notifications:

```typescript
// src/hook.ts L14-L25
export function createContext<T>(initialValue: T) {
  const context = {
    initialValue,
    Provider: (props: { value: T; children: any }) => {
      const ref = useRef(props.value)
      const subs = useMemo(() => new Set<() => void>(), [])
      
      if (ref.current !== props.value) {
        ref.current = props.value
        subs.forEach(sub => sub())
      }
      
      return props.children
    }
  }
  return context
}

```

The provider uses `useRef` to store the current value and `useMemo` to maintain a `Set` of subscriber callbacks. When the `value` prop changes, the provider updates the ref and iterates through all subscribers, triggering re-renders in consuming components.

### useContext: Consuming and Subscribing

The `useContext` implementation in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) (lines 29-40) handles subscriber registration and value retrieval:

```typescript
// src/hook.ts L29-L40
export function useContext<T>(context: any): T {
  const update = useReducer(null, null)[1]
  const fiber = useFiber()
  
  useEffect(() => {
    const provider = getBoundary(fiber, context.Provider)
    if (provider) {
      const subs = provider.hooks.list[1][0] as Set<() => void>
      subs.add(update)
      return () => subs.delete(update)
    }
  }, [context])
  
  const provider = getBoundary(fiber, context.Provider)
  return provider 
    ? (provider.hooks.list[0][0] as { current: T }).current 
    : context.initialValue
}

```

This hook uses `useReducer(null, null)` to generate a stable update function that serves as the subscriber callback. It then uses `useEffect` to register this callback with the nearest provider and returns a cleanup function that removes the subscription on unmount.

## Fiber Tree Traversal and Boundary Detection

Fre's context system relies on the fiber tree to locate providers. Two key utilities in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) enable this traversal.

### useFiber: Accessing the Current Fiber

The `useFiber` function (lines 27-28) provides hooks with access to the currently executing fiber:

```typescript
// src/reconcile.ts L27-L28
export const useFiber = () => currentFiber

```

This simple utility returns the global `currentFiber` variable, allowing `useContext` to know which component is currently rendering and where to start climbing the tree.

### getBoundary: Locating the Nearest Provider

The `getBoundary` function (lines 46-52) walks up the fiber parent chain to find the closest ancestor of a specific type:

```typescript
// src/reconcile.ts L46-L52
const getBoundary = (fiber: Fiber, type: any) => {
  while (fiber) {
    if (fiber.type === type) return fiber
    fiber = fiber.parent
  }
  return null
}

```

This linear traversal climbs from the current fiber to the root, checking each node's `type` against the context provider component. Once found, `useContext` accesses the provider's hook slots to retrieve the current value and subscriber set.

## The Subscription and Notification Flow

Understanding the complete data flow reveals how Fre achieves efficient context propagation without unnecessary re-renders:

1. **Provider initialization** – When a context provider renders, `createContext` stores the value in `fiber.hooks.list[0][0]` (a ref) and initializes an empty `Set` in `fiber.hooks.list[1][0]` for subscribers.

2. **Consumer registration** – When a component calls `useContext`, it generates an updater function via `useReducer`, then uses `useEffect` to add this updater to the provider's subscriber set. The effect returns a cleanup function that removes the subscriber on unmount.

3. **Value updates** – When the provider receives a new `value` prop, it compares it against the ref. If changed, it updates the ref and iterates through `subs`, calling each subscriber function.

4. **Re-render triggering** – The subscriber functions are the updaters returned by `useReducer(null, null)`. When invoked, they trigger a re-render of the consuming component, which then reads the new value from the provider's ref via `getBoundary`.

This design ensures that only components explicitly calling `useContext` re-render when the context changes, and the subscription mechanism leverages Fre's existing hook infrastructure without additional overhead.

## Practical Implementation Examples

### Defining a Context

Create a context using `createContext` with a default value:

```typescript
import { createContext } from "fre";

export const ThemeContext = createContext<string>("light");

```

### Providing Context Values

Wrap components with the Provider component returned by `createContext`:

```typescript
import { ThemeContext } from "./theme-context";
import { useState } from "fre";

function App() {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext value={theme}>
      <Toolbar setTheme={setTheme} />
    </ThemeContext>
  );
}

```

### Consuming Context

Use `useContext` to access the current value and subscribe to updates:

```typescript
import { useContext } from "fre";
import { ThemeContext } from "./theme-context";

function Toolbar({ setTheme }) {
  const theme = useContext(ThemeContext);

  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      Switch to {theme === "light" ? "dark" : "light"} mode
    </button>
  );
}

```

When the button triggers a theme change, the provider detects the new value, notifies all subscribers, and `Toolbar` re-renders with the updated theme.

## Summary

- **Fre implements useContext** using a subscriber pattern where providers maintain a Set of callback functions and consumers register updaters via useReducer.
- **createContext** generates a provider component that stores values in useRef and subscribers in useMemo, iterating through the subscriber set when values change.
- **useContext** leverages getBoundary to walk the fiber parent chain (src/reconcile.ts L46-L52) and locate the nearest provider, then reads the current value from the provider's hook slots.
- **Fiber traversal** occurs through useFiber (src/reconcile.ts L27-28) and getBoundary, enabling efficient O(depth) lookup of context providers without virtual DOM overhead.
- **Subscription lifecycle** is managed through useEffect, ensuring subscribers are added on mount and removed on unmount, preventing memory leaks and unnecessary updates.

## Frequently Asked Questions

### How does Fre's useContext differ from React's implementation?

Fre's useContext achieves the same semantic behavior as React—providing a way to pass data through the component tree without prop drilling—but uses a lighter-weight implementation. While React uses a separate context dependency system and potentially complex propagation heuristics, Fre implements context propagation through a simple subscriber Set stored in the provider's hook slot (`fiber.hooks.list[1][0]`). When the provider value changes, Fre iterates through this Set and calls each subscriber directly, triggering a re-render via the updater function created by `useReducer(null, null)`.

### Where does Fre store the context value and subscriber list?

Fre stores context data directly in the provider fiber's hook slots. According to the source code in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts), the current value lives in `fiber.hooks.list[0][0]` as a ref object (`{ current: T }`), while the subscriber Set resides in `fiber.hooks.list[1][0]`. This design leverages Fre's existing hook infrastructure—specifically the `list` array that stores hook state—eliminating the need for separate context storage mechanisms. When `useContext` calls `getBoundary` to find the provider, it accesses these slots directly to retrieve the current value and register new subscribers.

### How does Fre handle context updates and re-renders?

When a context provider receives a new `value` prop, the component created by `createContext` compares the new value against the previous one stored in a `useRef`. If the values differ, the ref updates and the component iterates through the `subs` Set (stored in `fiber.hooks.list[1][0]`), calling each subscriber function. These subscriber functions are the updaters returned by `useReducer(null, null)` in consuming components. Invoking an updater triggers Fre's reconciliation process for that specific fiber, causing the component to re-render and call `useContext` again, which retrieves the updated value from the provider's ref in `fiber.hooks.list[0][0]`.

### What files contain the core context implementation in Fre?

The context system spans three primary files in Fre's source code. **[`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts)** contains the main implementation including `createContext` (lines 14-25) and `useContext` (lines 29-40), along with hook slot management utilities. **[`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts)** provides the fiber traversal mechanisms through `useFiber` (lines 27-28) and `getBoundary` (lines 46-52), which enable `useContext` to locate provider components in the tree. **[`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts)** defines the TypeScript interfaces including `ContextType`, `Fiber`, and `HookList` that shape the data structures used throughout the context system. Together, these files implement a complete context propagation mechanism in under 100 lines of code.