Skip to content

React DOM APIs

Fresh

The react-dom package provides DOM-specific methods for web applications.

Client APIs

createRoot

Creates a root to display React components inside a browser DOM node.

jsx
import { createRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = createRoot(domNode);
root.render(<App />);

Returns: An object with render and unmount methods.

root.render(reactNode) - Displays JSX in the root:

jsx
root.render(<App />);

// Later, update with different JSX:
root.render(<App counter={2} />);

root.unmount() - Destroys the rendered tree:

jsx
root.unmount();

hydrateRoot

Lets you display React components inside a browser DOM node whose HTML content was previously generated by react-dom/server.

jsx
import { hydrateRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, <App />);

React will attach to the HTML that exists inside domNode and take over managing the DOM. An app fully built with React will usually only have one hydrateRoot call.

createPortal

Renders children into a different part of the DOM:

jsx
import { createPortal } from 'react-dom';

function Modal({ children }) {
  return createPortal(
    <div className="modal">{children}</div>,
    document.getElementById('modal-root')
  );
}

A portal only changes the physical placement of the DOM node. The JSX rendered into a portal acts as a child of the React component that renders it. Events bubble up through the React tree, not the DOM tree.

Common use cases:

  • Modals and dialogs
  • Tooltips and popovers
  • Floating menus
  • Widgets that need to "break out" of their container

flushSync

Forces React to flush any pending state updates synchronously:

jsx
import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCount(c => c + 1);
  });
  // DOM is updated here
  console.log(document.getElementById('counter').textContent);
}

WARNING

flushSync can significantly hurt performance. Use sparingly. It forces pending Suspense boundaries to show their fallback.

Resource Preloading APIs

preconnect

jsx
import { preconnect } from 'react-dom';
preconnect('https://example.com');

preload

jsx
import { preload } from 'react-dom';
preload('https://example.com/font.woff2', { as: 'font', type: 'font/woff2' });

prefetchDNS

jsx
import { prefetchDNS } from 'react-dom';
prefetchDNS('https://example.com');

preinit

jsx
import { preinit } from 'react-dom';
preinit('https://example.com/script.js', { as: 'script' });

DOM Components

React supports all built-in browser HTML and SVG components. Special props:

Common Props (all components)

PropDescription
classNameCSS class (string)
styleInline styles (object with camelCase properties)
refRef object or callback
childrenChild elements
dangerouslySetInnerHTMLRaw HTML ({ __html: '<p>html</p>' })
keyUnique identifier for list items

Form Components

<input> - Controlled vs uncontrolled:

jsx
// Controlled (React manages value)
<input value={text} onChange={e => setText(e.target.value)} />

// Uncontrolled (DOM manages value)
<input defaultValue="initial" ref={inputRef} />

<textarea> - Uses value prop (not children):

jsx
<textarea value={text} onChange={e => setText(e.target.value)} />

<select> - Uses value on <select>, not selected on <option>:

jsx
<select value={selectedFruit} onChange={e => setSelectedFruit(e.target.value)}>
  <option value="apple">Apple</option>
  <option value="banana">Banana</option>
</select>

<form> - Can use the action prop with Server Actions:

jsx
<form action={submitAction}>
  <input name="query" />
  <button type="submit">Search</button>
</form>