---
title: "DOM Rendering"
description: "DOM Rendering is a lightweight, runtime-focused engine within the Hono framework designed to bridge the gap between declarative JSX templates and imperative browser DOM manipulations. Unlike server..."
last_updated: "2026-07-02T09:13:47.233734+00:00"
canonical_url: "https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/rendering-jsx/dom-rendering"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [src/jsx/dom/render.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts)
- [src/jsx/base.ts](https://github.com/blade47/hono/blob/main/src/jsx/base.ts)
- [src/context.ts](https://github.com/blade47/hono/blob/main/src/context.ts)
- [src/jsx/hooks/index.ts](https://github.com/blade47/hono/blob/main/src/jsx/hooks/index.ts)
- [src/jsx/streaming.ts](https://github.com/blade47/hono/blob/main/src/jsx/streaming.ts)
- [src/jsx/components.ts](https://github.com/blade47/hono/blob/main/src/jsx/components.ts)
- [src/jsx/dom/intrinsic-element/components.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/intrinsic-element/components.ts)
- [src/jsx/dom/client.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/client.ts)
- [src/jsx/intrinsic-element/components.ts](https://github.com/blade47/hono/blob/main/src/jsx/intrinsic-element/components.ts)
- [src/middleware/jsx-renderer/index.ts](https://github.com/blade47/hono/blob/main/src/middleware/jsx-renderer/index.ts)
- [src/jsx/jsx-runtime.ts](https://github.com/blade47/hono/blob/main/src/jsx/jsx-runtime.ts)
- [src/jsx/context.ts](https://github.com/blade47/hono/blob/main/src/jsx/context.ts)
- [src/jsx/dom/server.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/server.ts)
- [src/jsx/dom/index.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/index.ts)
- [src/jsx/dom/css.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/css.ts)
- [src/jsx/index.ts](https://github.com/blade47/hono/blob/main/src/jsx/index.ts)
- [src/jsx/dom/jsx-runtime.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/jsx-runtime.ts)
- [src/jsx/dom/jsx-dev-runtime.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/jsx-dev-runtime.ts)
- [src/utils/html.ts](https://github.com/blade47/hono/blob/main/src/utils/html.ts)
- [src/jsx/jsx-dev-runtime.ts](https://github.com/blade47/hono/blob/main/src/jsx/jsx-dev-runtime.ts)
- [src/hono-base.ts](https://github.com/blade47/hono/blob/main/src/hono-base.ts)
- [src/helper/css/index.ts](https://github.com/blade47/hono/blob/main/src/helper/css/index.ts)
- [src/jsx/dom/components.ts](https://github.com/blade47/hono/blob/main/src/jsx/dom/components.ts)
- [src/jsx/types.ts](https://github.com/blade47/hono/blob/main/src/jsx/types.ts)
- [src/jsx/intrinsic-elements.ts](https://github.com/blade47/hono/blob/main/src/jsx/intrinsic-elements.ts)
</details>

DOM Rendering is a lightweight, runtime-focused engine within the Hono framework designed to bridge the gap between declarative JSX templates and imperative browser DOM manipulations. Unlike server-side rendering (SSR) implementations that serialize JSX into HTML strings, this subsystem maintains an internal virtual-tree representation of nodes, enabling efficient updates, hook management, and dynamic interaction within the client-side environment. It is the core mechanism that allows Hono applications to remain responsive without the overhead of heavy virtual DOM reconciliation found in traditional libraries.

The architecture centers around the `Node` structure—a unified interface that distinguishes between static text nodes and interactive element nodes. By tracking previous props (`pP`) and virtual children (`vC`), the renderer minimizes browser reflows through surgical DOM updates. It supports React-like hooks such as `useState`, `useEffect`, and `useMemo`, which are integrated directly into the lifecycle of each node object, allowing state-driven UI updates to occur precisely where needed without traversing the entire component tree.

Interaction with the system is typically handled through the `render()` or `hydrateRoot()` APIs, which initiate the transformation of a JSX structure into a live DOM tree. Once rendered, the renderer automatically manages effect cleanup, event binding, and context propagation, ensuring that declarative updates result in predictable DOM transitions. This design prioritizes runtime performance, using native browser capabilities and minimal overhead to handle the complexities of component-based UI development.

## The Node Object Model
The core of the DOM renderer is the `NodeObject` type, a recursive data structure representing an element in the DOM tree. Unlike a simple HTML string, the `NodeObject` caches runtime metadata required for reconciliation and reactive updates.

| Field | Purpose |
| :--- | :--- |
| `props` | Current component properties. |
| `pP` | Previous properties, used to detect changes for incremental updates. |
| `vC` | Virtual children: an array of nested `Node` instances. |
| `e` | The live `SupportedElement` or `Text` node associated with this entry. |
| `c` | The parent `Container` (HTMLElement or DocumentFragment). |
| `s` | A flag indicating whether to skip build/apply steps (performance optimization). |
| `[DOM_STASH]` | An internal storage array for hooks (index, effects, context). |

Sources: [src/jsx/dom/render.ts:41-65](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L41-L65)

## Control Flow: Reconciliation and Building
The renderer process uses a two-phase cycle: `build` (determining the structure) and `apply` (syncing that structure to the live DOM). The `build` function recursively transforms JSX nodes into the `Node` internal representation.

When a node requires an update, the renderer uses a `WeakMap` (`updateMap`) to consolidate pending changes, ensuring that rapid state updates trigger only the necessary re-rendering. During the `build` phase, the system maintains a `buildDataStack` to track the context and the current node being processed, which is essential for `useContext` and hook initialization.

```mermaid
flowchart TD
    A["Render Call"] --> B["Build Phase"]
    B --> C["Recursive VDOM Construction"]
    C --> D["Identify Changes (Diffing)"]
    D --> E["Apply Phase"]
    E --> F["DOM Mutation (Append/Insert/Update)"]
    F --> G["Execute Effects (useEffect/useLayoutEffect)"]
```
Sources: [src/jsx/dom/render.ts:497-665](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L497-L665), [src/jsx/dom/render.ts:740-781](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L740-L781)

## Hook Execution and Lifecycle
Hooks are indexed within the `[DOM_STASH]` field of a `NodeObject`. The system utilizes a `current hook index` to ensure that consecutive calls to hooks within a component remain synchronized across re-renders.

> [!NOTE]
> `useEffect` callbacks are scheduled via `requestAnimationFrame` to decouple DOM mutations from effect execution, while `useLayoutEffect` runs synchronously after the DOM is updated but before the browser paints.

The system ensures correct cleanup via the `removeNode` function, which explicitly triggers registered effect cleanups and clears references stored in the `refCleanupMap`.

Sources: [src/jsx/dom/render.ts:341-362](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L341-L362), [src/jsx/hooks/index.ts:260-287](https://github.com/blade47/hono/blob/main/src/jsx/hooks/index.ts#L260-L287)

## Attribute and Event Handling
The renderer optimizes property application by distinguishing between event listeners, special attributes (like `dangerouslySetInnerHTML`), and standard DOM attributes.

- **Event Delegation:** Attributes starting with "on" (e.g., `onClick`) are parsed into `[eventName, capture]` pairs. The system maintains an `eventCache` for frequently used events to speed up lookups.
- **Form Values:** Special handling is provided for `SELECT` and `INPUT` fields, where `applySelectValue` or direct property assignment is used instead of standard `setAttribute` to reflect internal states like `selectedIndex` or `checked`.
- **Attribute Application:** Standard attributes are set using `container.setAttribute`. The renderer uses a `try-catch` block around `setAttribute` specifically to catch `InvalidCharacterError`, avoiding a slow upfront regex validation of every attribute name.

Sources: [src/jsx/dom/render.ts:115-132](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L115-L132), [src/jsx/dom/render.ts:164-272](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L164-L272)

## Error Boundaries and Streaming
The DOM renderer integrates with `ErrorBoundary` components by maintaining an error stack in the `Context`. When a component throws an error, the renderer looks for the closest `DOM_ERROR_HANDLER` in the hierarchy.

If an error boundary is found, the system wraps the fallback in an update queue. This allows the boundary to recover gracefully. The renderer uses a `cancelBuild` symbol to halt the reconciliation process for subtrees that have crashed, preventing them from contaminating the global DOM state until a recovery render is triggered.

Sources: [src/jsx/dom/render.ts:612-657](https://github.com/blade47/hono/blob/main/src/jsx/dom/render.ts#L612-L657)

## Worked Example: Client-Side Rendering
To render an application, use the `createRoot` API. This pattern creates a consistent root-level controller that can manage subsequent updates via `useState`.

```typescript
import { createRoot } from 'hono/jsx/dom/client';

const App = ({ name }: { name: string }) => <h1>Hello {name}</h1>;

const root = createRoot(document.getElementById('root')!);
root.render(<App name="Hono" />);

// Later, trigger an update if the component was setup to be reactive
```
Sources: [src/jsx/dom/client.ts:23-66](https://github.com/blade47/hono/blob/main/src/jsx/dom/client.ts#L23-L66)

## Related

- [JSX Renderer](https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/rendering-jsx/jsx-renderer)


## Sitemap

See the full [sitemap](https://www.doc0.app/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/llms.txt) for all pages in this wiki.
