{"id":16512,"library":"react-reconciler","title":"React Reconciler for Custom Renderers","description":"react-reconciler is an experimental, low-level package provided by the React team for developers who wish to build custom React renderers for non-standard environments, such as WebGL, terminal UIs, or native desktop applications. It encapsulates the core reconciliation logic (the 'Fiber' architecture since React 16) responsible for efficiently diffing component trees and determining the minimal set of changes needed to update the UI. The package, currently at version 0.33.0, is designed to be pluggable with a custom `HostConfig` object, allowing developers to define platform-specific operations for creating, updating, and deleting nodes. Its API is explicitly unstable and does not adhere to React's standard versioning scheme, meaning breaking changes can occur in minor versions. It requires `react` as a peer dependency, specifically `>=19.2.0` for this version. It supports two primary modes: 'mutation mode' for environments that modify existing nodes (like the DOM) and 'persistent mode' for immutable tree structures.","status":"active","version":"0.33.0","language":"javascript","source_language":"en","source_url":"https://github.com/facebook/react","tags":["javascript","react"],"install":[{"cmd":"npm install react-reconciler","lang":"bash","label":"npm"},{"cmd":"yarn add react-reconciler","lang":"bash","label":"yarn"},{"cmd":"pnpm add react-reconciler","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core React library for defining components and elements. react-reconciler operates on React elements.","package":"react","optional":false}],"imports":[{"note":"The primary factory function is typically imported as the default export. Many examples name the imported value 'Reconciler' or 'createReconciler'. For CommonJS, use `const Reconciler = require('react-reconciler');`","wrong":"import { createReconciler } from 'react-reconciler';","symbol":"createReconciler","correct":"import Reconciler from 'react-reconciler';"},{"note":"Event priority constants, crucial for implementing `getCurrentEventPriority` in `HostConfig`, are imported from a specific subpath.","wrong":"import { DefaultEventPriority } from 'react-reconciler';","symbol":"DiscreteEventPriority, ContinuousEventPriority, DefaultEventPriority","correct":"import { DiscreteEventPriority, ContinuousEventPriority, DefaultEventPriority } from 'react-reconciler/constants';"},{"note":"While `HostConfig` is a conceptual interface for configuring the renderer, it's not directly exported as a value for runtime import. It's a type definition; for actual implementation, you define an object conforming to it.","wrong":"import { HostConfig } from 'react-reconciler';","symbol":"HostConfig (interface/type)","correct":"import type { HostConfig } from 'react-reconciler';"}],"quickstart":{"code":"import React from 'react';\nimport Reconciler from 'react-reconciler';\n\n// Minimal HostConfig for a 'console' renderer\nconst hostConfig = {\n  now: Date.now,\n  supportsMutation: true, // Or supportsPersistence: true for immutable trees\n  getRootHostContext: () => ({}),\n  getChildHostContext: (parentHostContext, type) => parentHostContext,\n  getPublicInstance: (instance) => instance,\n  prepareForCommit: () => null,\n  resetAfterCommit: () => {},\n\n  createInstance: (type, props, rootContainerInstance, hostContext, internalInstanceHandle) => {\n    // In a real renderer, this would create a DOM node, canvas object, etc.\n    const instance = { type, props, children: [] };\n    console.log(`CREATE: <${type}>`, props);\n    return instance;\n  },\n  createTextInstance: (text, rootContainerInstance, hostContext, internalInstanceHandle) => {\n    console.log(`CREATE TEXT: \"${text}\"`);\n    return text;\n  },\n  appendInitialChild: (parentInstance, child) => {\n    parentInstance.children.push(child);\n    console.log(`APPEND INITIAL CHILD:`, { parent: parentInstance.type, child });\n  },\n  appendChild: (parentInstance, child) => {\n    parentInstance.children.push(child);\n    console.log(`APPEND CHILD:`, { parent: parentInstance.type, child });\n  },\n  insertBefore: (parentInstance, child, beforeChild) => {\n    const index = parentInstance.children.indexOf(beforeChild);\n    if (index > -1) {\n      parentInstance.children.splice(index, 0, child);\n    }\n    console.log(`INSERT BEFORE:`, { parent: parentInstance.type, child, before: beforeChild });\n  },\n  removeChild: (parentInstance, child) => {\n    parentInstance.children = parentInstance.children.filter(c => c !== child);\n    console.log(`REMOVE CHILD:`, { parent: parentInstance.type, child });\n  },\n  prepareUpdate: (instance, type, oldProps, newProps, rootContainerInstance, hostContext) => {\n    // Compare oldProps and newProps to determine what needs to change.\n    const payload = {};\n    for (const key in newProps) {\n      if (newProps[key] !== oldProps[key]) {\n        payload[key] = newProps[key];\n      }\n    }\n    return Object.keys(payload).length > 0 ? payload : null;\n  },\n  commitUpdate: (instance, updatePayload, type, oldProps, newProps, internalHandle) => {\n    Object.assign(instance.props, updatePayload);\n    console.log(`UPDATE: <${type}>`, updatePayload);\n  },\n  commitTextUpdate: (textInstance, oldText, newText) => {\n    console.log(`TEXT UPDATE: \"${oldText}\" -> \"${newText}\"`);\n    Object.assign(textInstance, newText); // Assuming textInstance is mutable string or wrapper\n  },\n  shouldSetTextContent: (type, props) => typeof props.children === 'string' || typeof props.children === 'number',\n  // Add other required methods like scheduleTimeout, cancelTimeout, noTimeout, etc.\n  scheduleTimeout: setTimeout,\n  cancelTimeout: clearTimeout,\n  noTimeout: -1,\n  isPrimaryRenderer: true,\n  getCurrentEventPriority: () => 3, // DefaultEventPriority (requires import from 'react-reconciler/constants')\n  // A minimal set of methods is still quite large. Many can be no-ops initially.\n};\n\nconst MyRenderer = Reconciler(hostConfig);\n\nconst rootContainer = {}; // Represents the 'host' root, e.g., a canvas element, a terminal object\nconst root = MyRenderer.createContainer(rootContainer, false, false); // No hydrate, no hydrationCallbacks\n\nexport function render(element, container) {\n  // `container` here would be the root object in your host environment\n  // like `document.getElementById('root')` for ReactDOM.\n  // For this console example, we'll just use the `rootContainer` created above.\n  MyRenderer.updateContainer(element, root, null, () => {\n    console.log('RENDER COMPLETE!', rootContainer);\n    // For a console renderer, you'd print the final tree here.\n    // In a real app, this would trigger a flush of pending updates to the actual UI.\n  });\n}\n\n// Example usage\nfunction App() {\n  const [count, setCount] = React.useState(0);\n  React.useEffect(() => {\n    const interval = setInterval(() => setCount(c => c + 1), 1000);\n    return () => clearInterval(interval);\n  }, []);\n\n  return (\n    <box color=\"blue\">\n      <text>{`Count: ${count}`}</text>\n      <button onClick={() => setCount(0)}>Reset</button>\n    </box>\n  );\n}\n\nrender(<App />, rootContainer);\n","lang":"typescript","description":"This example demonstrates how to create a basic custom React renderer using `react-reconciler`. It defines a minimal `HostConfig` for a 'console' environment, logs lifecycle events, and shows how to use `createContainer` and `updateContainer` to render a simple React component."},"warnings":[{"fix":"Always pin `react-reconciler` to an exact version and thoroughly test custom renderers after any updates. Monitor React's official GitHub repository for changes to the internal reconciler code.","message":"The `react-reconciler` package is explicitly marked as experimental and unstable. Its API does not follow React's usual semantic versioning, and breaking changes can occur without major version bumps. Users should expect to adapt their custom renderers frequently.","severity":"breaking","affected_versions":">=0.1.0"},{"fix":"Refer to existing open-source custom renderers (e.g., React Three Fiber, Ink) and the internal React DOM/Native host configs for comprehensive examples. Start with a minimal set of methods and incrementally add functionality, testing at each step.","message":"Implementing a complete and performant `HostConfig` is a highly complex task, requiring deep understanding of React's Fiber architecture and the target platform's rendering specifics. Missing or incorrectly implemented methods can lead to subtle bugs, memory leaks, or poor performance.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Understand your target platform's rendering model. If your environment modifies existing nodes (like the DOM), use `supportsMutation`. If it generates new immutable trees on every update, use `supportsPersistence`.","message":"Choosing between `supportsMutation: true` and `supportsPersistence: true` in your `HostConfig` is critical. Incorrectly selecting the mode (e.g., mutation for an immutable tree) will lead to incorrect rendering, performance issues, and potential crashes.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Ensure your project's `react` dependency satisfies the peer dependency range of `react-reconciler`. Use `npm install --force` or `yarn add --force` with caution if necessary, but ideally resolve conflicts cleanly.","message":"The peer dependency `react` must match the expected major version for `react-reconciler`. For version 0.33.0, this is `^19.2.0`. Mismatched React versions can lead to runtime errors or unexpected behavior due to internal API differences.","severity":"gotcha","affected_versions":">=0.33.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"You should not be directly importing internal React modules like `ReactFiberConfig`. Instead, you define a `HostConfig` object and pass it to the `Reconciler` factory function.","cause":"Attempting to import internal `ReactFiberConfig` directly or a similar internal React module that expects a renderer-specific shim.","error":"Error: This module must be shimmed by a specific renderer."},{"fix":"Always provide a stable, unique `key` prop for each item in a list. This key should ideally come from the data itself, not the array index, especially if the list can be reordered, filtered, or grow.","cause":"While a general React warning, it's critical for custom renderers as it directly impacts the reconciliation algorithm's performance when diffing lists of elements. Using array indices as keys for dynamic lists is a common cause.","error":"Warning: Each child in a list should have a unique 'key' prop."},{"fix":"Verify that `MyRenderer.updateContainer(element, root, null, callback)` is invoked whenever the root element changes. Debug your `HostConfig` methods to ensure they correctly translate React's instructions into operations on your host environment's instances.","cause":"In a custom renderer, this often means `updateContainer` is not called correctly after state changes, or the `HostConfig` methods (e.g., `commitUpdate`, `commitTextUpdate`, `appendChild`, `removeChild`) are not implemented to correctly apply changes to the host environment.","error":"UI doesn't update or renders inconsistently after state changes."}],"ecosystem":"npm"}