# Client-Side Rendering

Client-side rendering APIs for modern React applications using concurrent rendering features.

## Capabilities

### createRoot

Creates a React root for rendering React components inside a browser DOM element with concurrent features enabled.

```javascript { .api }
/**
 * Create a React root for concurrent rendering
 * @param container - DOM element or DocumentFragment to render into
 * @param options - Optional configuration for the root
 * @returns Root object with render() and unmount() methods
 */
function createRoot(
  container: Element | DocumentFragment,
  options?: CreateRootOptions
): Root;

interface CreateRootOptions {
  /** Enable React strict mode checks */
  unstable_strictMode?: boolean;
  /** Prefix for IDs generated by useId hook */
  identifierPrefix?: string;
  /** Handler for uncaught errors that escape all error boundaries */
  onUncaughtError?: (error: Error, errorInfo: ErrorInfo) => void;
  /** Handler for errors caught by error boundaries */
  onCaughtError?: (error: Error, errorInfo: ErrorInfo) => void;
  /** Handler for errors React can recover from automatically */
  onRecoverableError?: (error: Error, errorInfo: ErrorInfo) => void;
  /** Experimental callback for default transition indicator (can return cleanup function) */
  onDefaultTransitionIndicator?: () => void | (() => void);
  /** Experimental transition callbacks for React DevTools */
  unstable_transitionCallbacks?: TransitionTracingCallbacks;
}

interface Root {
  /** Render React elements into the root */
  render(children: ReactNode): void;
  /** Unmount the root and cleanup all resources */
  unmount(): void;
}

interface ErrorInfo {
  /** Stack trace of React components involved in the error */
  componentStack?: string;
  /** The error boundary component that caught the error (if onCaughtError) */
  errorBoundary?: React.Component;
}
```

**Usage Examples:**

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

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

// With options
const root = createRoot(document.getElementById('root'), {
  identifierPrefix: 'my-app-',
  onUncaughtError(error, errorInfo) {
    console.error('Uncaught error:', error);
    console.error('Component stack:', errorInfo.componentStack);
    // Report to error tracking service
  },
  onRecoverableError(error, errorInfo) {
    console.warn('Recoverable error:', error);
    // Log but don't necessarily show to user
  }
});
root.render(<App />);

// Update rendered content
root.render(<App updatedProp="value" />);

// Cleanup when done
root.unmount();
```

**Key Features:**

- **Concurrent Rendering**: Enables React 18's concurrent features like useTransition, useDeferredValue
- **Automatic Batching**: All state updates are automatically batched for better performance
- **Error Handling**: Configure custom error handlers for different error scenarios
- **Progressive Updates**: React can interrupt long-running renders to handle urgent updates
- **Suspense Support**: Full support for Suspense boundaries and lazy loading

**Migration from React 17:**

```javascript
// Old (React 17)
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));

// New (React 18+)
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
```

**Notes:**

- The container's existing children will be replaced when you call `render()` for the first time
- Calling `render()` multiple times updates the existing render
- `unmount()` removes all event handlers and state, and stops all pending work
- After calling `unmount()`, you cannot use the root again; create a new root if needed

### hydrateRoot

Attaches React to server-rendered HTML inside a browser DOM element, preserving the existing markup and making it interactive.

```javascript { .api }
/**
 * Hydrate server-rendered HTML with React
 * @param container - DOM element containing server-rendered content
 * @param initialChildren - React elements matching the server content
 * @param options - Optional configuration for hydration
 * @returns Root object with render() and unmount() methods
 */
function hydrateRoot(
  container: Element | Document,
  initialChildren: ReactNode,
  options?: HydrateRootOptions
): Root;

interface HydrateRootOptions extends CreateRootOptions {
  /** Called when a Suspense boundary completes hydration */
  onHydrated?: (suspenseBoundary: SuspenseBoundary) => void;
  /** Called when a Suspense boundary is deleted during hydration */
  onDeleted?: (suspenseBoundary: SuspenseBoundary) => void;
  /** Form state from server rendering (for Server Actions) */
  formState?: ReactFormState;
}

type SuspenseBoundary = Comment; // HTML comment node marking boundary
```

**Usage Examples:**

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

// Basic hydration
hydrateRoot(document.getElementById('root'), <App />);

// With options
hydrateRoot(
  document.getElementById('root'),
  <App />,
  {
    onRecoverableError(error, errorInfo) {
      console.error('Hydration error:', error);
      // Log hydration mismatches
    },
    onHydrated(suspenseBoundary) {
      console.log('Suspense boundary hydrated:', suspenseBoundary);
    }
  }
);

// Hydrating the entire document
hydrateRoot(document, <App />);
```

**Hydration with Server Actions:**

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

// Pass formState from server to enable Server Actions
const formState = window.__SERVER_FORM_STATE__;
hydrateRoot(
  document.getElementById('root'),
  <App />,
  { formState }
);
```

**Key Features:**

- **Preserves Markup**: Reuses existing server-rendered HTML instead of recreating it
- **Fast Initial Render**: Page is visible and interactive faster than client-only rendering
- **Progressive Hydration**: Can hydrate in chunks with Suspense boundaries
- **Error Recovery**: Automatically recovers from minor hydration mismatches
- **Server Actions Support**: Can hydrate forms with Server Actions

**Hydration Process:**

1. React attaches event listeners to existing DOM nodes
2. Runs effects and state initialization
3. Verifies server and client content match
4. Makes the page interactive
5. Continues hydrating deferred content in Suspense boundaries

**Common Hydration Issues:**

```javascript
// ❌ Problem: Mismatch between server and client
function Component() {
  // Date.now() or Math.random() will differ between server and client
  return <div>{Date.now()}</div>;
}

// ✅ Solution: Use useEffect for client-only content
function Component() {
  const [date, setDate] = useState(null);

  useEffect(() => {
    setDate(Date.now());
  }, []);

  return <div>{date ?? 'Loading...'}</div>;
}

// ✅ Solution: Suppress hydration warning for known mismatches
function Component() {
  return <div suppressHydrationWarning>{Date.now()}</div>;
}
```

**Best Practices:**

1. **Ensure Identical Content**: Server and client must render the same content initially
2. **Handle Client-Only Content**: Use `useEffect` or `useState` for client-only data
3. **Use suppressHydrationWarning**: Only for intentional mismatches like timestamps
4. **Monitor Errors**: Configure `onRecoverableError` to track hydration issues
5. **Progressive Hydration**: Wrap deferred content in `<Suspense>` boundaries

**Notes:**

- Hydration warnings indicate server/client mismatches that can cause bugs
- React will try to recover from hydration errors by client-side rendering
- Hydration is synchronous - the page will be blocked until React attaches
- Use `<Suspense>` for progressive/selective hydration of deferred content

### Root Methods

Both `createRoot` and `hydrateRoot` return a Root object with these methods:

#### render

```javascript { .api }
/**
 * Render or update React elements in the root
 * @param children - React elements to render
 */
root.render(children: ReactNode): void;
```

**Usage:**

```javascript
const root = createRoot(document.getElementById('root'));

// Initial render
root.render(<App />);

// Update render
root.render(<App newProp="value" />);

// Render different content
root.render(<DifferentApp />);

// Remove content (alternative to unmount)
root.render(null);
```

**Notes:**

- First call replaces all existing children in the container
- Subsequent calls update the existing render
- React efficiently updates only what changed
- Rendering `null` removes all content but keeps the root mounted

#### unmount

```javascript { .api }
/**
 * Unmount the root and cleanup all resources
 */
root.unmount(): void;
```

**Usage:**

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

// Cleanup
root.unmount();

// Container is now empty and all event handlers are removed
```

**Notes:**

- Calls cleanup functions from all effects
- Removes all event listeners
- Stops all pending async work
- Clears all state and references
- Cannot reuse the root after unmounting; create a new one if needed

## Error Handling

React DOM provides multiple error handlers for different scenarios:

### onUncaughtError

Called when an error escapes all error boundaries and reaches the root.

```javascript
createRoot(container, {
  onUncaughtError(error, errorInfo) {
    // Error that crashed the entire app
    console.error('Fatal error:', error);
    reportToErrorService(error, errorInfo.componentStack);
  }
});
```

### onCaughtError

Called when an error is caught by an error boundary.

```javascript
createRoot(container, {
  onCaughtError(error, errorInfo) {
    // Error caught by error boundary
    console.log('Caught error:', error);
    console.log('Boundary:', errorInfo.errorBoundary);
    logNonFatalError(error);
  }
});
```

### onRecoverableError

Called when React automatically recovers from an error (e.g., hydration mismatch).

```javascript
createRoot(container, {
  onRecoverableError(error, errorInfo) {
    // React recovered but you should still log it
    console.warn('Recovered from:', error);
    logRecoverableError(error, errorInfo.componentStack);
  }
});
```

## Performance Considerations

### Identifier Prefix

Use `identifierPrefix` when rendering multiple React roots on the same page to avoid ID collisions:

```javascript
const headerRoot = createRoot(document.getElementById('header'), {
  identifierPrefix: 'header-'
});

const mainRoot = createRoot(document.getElementById('main'), {
  identifierPrefix: 'main-'
});

// IDs from useId will be prefixed: 'header-:r1:', 'main-:r1:', etc.
```

### Concurrent Features

`createRoot` enables concurrent features that improve responsiveness:

```javascript
import { useState, useTransition } from 'react';

function SearchResults() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  function handleChange(e) {
    // Urgent: show what was typed
    setQuery(e.target.value);

    // Non-urgent: update search results
    startTransition(() => {
      setResults(searchResults(e.target.value));
    });
  }

  return <input value={query} onChange={handleChange} />;
}
```

## Version

```javascript { .api }
const version: string; // "19.2.0"
```

All client rendering APIs export the React DOM version string.

## Types

### ReactNode

```javascript { .api }
type ReactNode =
  | ReactElement
  | string
  | number
  | boolean
  | null
  | undefined
  | ReactNode[];
```

### TransitionTracingCallbacks

```javascript { .api }
interface TransitionTracingCallbacks {
  onTransitionStart?: (transitionName: string, startTime: number) => void;
  onTransitionProgress?: (
    transitionName: string,
    startTime: number,
    currentTime: number,
    pending: Array<{ name: string }>
  ) => void;
  onTransitionComplete?: (
    transitionName: string,
    startTime: number,
    endTime: number
  ) => void;
  onMarkerProgress?: (
    transitionName: string,
    marker: string,
    startTime: number,
    currentTime: number,
    pending: Array<{ name: string }>
  ) => void;
  onMarkerComplete?: (
    transitionName: string,
    marker: string,
    startTime: number,
    endTime: number
  ) => void;
}
```

**Note**: These callbacks are unstable and primarily used by React DevTools for transition profiling.
