# @remix-run/dom

DOM rendering and JSX runtime for Remix UI. Provides efficient virtual DOM diffing, event handling, and HTML element support for browser environments.

## Features

- **JSX Runtime**: Full JSX support with HTML intrinsic elements and attributes
- **Efficient Diffing**: Optimized virtual DOM diffing algorithm inspired by Preact
- **Event System Integration**: Seamless integration with `@remix-run/events`
- **Style Processing**: Automatic CSS-in-JS handling via `@remix-run/style`
- **Ref Support**: Stable ref callbacks for DOM element access
- **Component Lifecycle**: Proper cleanup and unmounting support

## API

### `render(element, container)`

Mounts a Remix UI element to a DOM container with efficient diffing and updates.

```ts
import { render } from "@remix-run/dom";

// Mount your app to a DOM element
let cleanup = render(<App />, document.getElementById("root"));

// Later: cleanup and unmount
cleanup();
```

### `flush()`

Forces synchronous flushing of any pending render updates. Useful for testing or when you need immediate DOM updates.

```ts
import { flush } from '@remix-run/dom'

// After state changes
flush() // DOM is now fully updated
```

## JSX Runtime

The DOM package provides the JSX runtime for browser environments, extending the base JSX from `@remix-run/component` with HTML elements and attributes.

### TypeScript Configuration

```json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@remix-run/dom"
  }
}
```

### HTML Elements

All standard HTML elements are supported with full type safety:

```tsx
<div className="container">
  <h1>Welcome</h1>
  <p>
    Hello, <strong>world</strong>!
  </p>
  <button type="button" disabled={isLoading}>
    Click me
  </button>
</div>
```

### Event Handling

Events use the `on` prop with `@remix-run/events`:

```tsx
import { dom } from '@remix-run/events'

let button = (
  <button
    on={[
      dom.click(() => console.log('Clicked')),
      dom.keydown((e) => {
        if (e.key === 'Enter') handleSubmit()
      }),
    ]}
  >
    Submit
  </button>
)
```

### Style Prop

The `style` prop accepts CSS-in-JS objects via `@remix-run/style`:

```tsx
<div
  style={{
    display: 'flex',
    padding: '16px',
    '&:hover': {
      backgroundColor: '#f0f0f0',
    },
  }}
>
  Hover me
</div>
```

### Refs

Refs provide stable access to DOM elements:

```tsx
<input
  ref={(element) => {
    if (element) {
      element.focus()
    }
  }}
/>
```

## HTML Attribute Types

### Props Utility Type

Create components that accept HTML attributes with the `Props` utility:

```tsx
import type { Props } from '@remix-run/dom'

interface ButtonProps extends Props<'button', { variant?: 'primary' | 'secondary' }> {}

function Button({ variant = 'primary', children, ...rest }: ButtonProps) {
  return (
    <button {...rest} className={`btn btn-${variant}`}>
      {children}
    </button>
  )
}

// Usage - all button HTML attributes are available
let button = (
  <Button variant="primary" type="submit" disabled={isLoading} aria-label="Submit form">
    Submit
  </Button>
)
```

### Specific Element Types

Import specific HTML attribute types when needed:

```tsx
import type {
  ButtonHTMLAttributes,
  InputHTMLAttributes,
  AnchorHTMLAttributes,
} from '@remix-run/dom'
```

## Diffing Algorithm

The DOM package uses an efficient virtual DOM diffing algorithm:

- **Keyed Lists**: Efficient reordering with minimal DOM operations
- **Component State**: Preserves component state across renders
- **Minimal Updates**: Only touches changed DOM properties
- **Batch Updates**: Automatic batching of multiple state changes

## Integration with Remix UI

This package provides the DOM rendering layer for the Remix UI component system:

```tsx
import type { Handle } from '@remix-run/component'
import { render } from '@remix-run/dom'

function Counter(this: Handle) {
  let count = 0

  return () => (
    <div>
      <p>Count: {count}</p>
      <button
        on={dom.click(() => {
          count++
          this.render()
        })}
      >
        Increment
      </button>
    </div>
  )
}

// Mount to DOM
render(<Counter />, document.getElementById('app'))
```

## Server-Side Rendering

While this package is designed for browser environments, it can be used in SSR setups:

```tsx
// Server: render HTML with embedded <style data-remix-style>
import { renderToString } from '@remix-run/dom/server'

let { html } = renderToString(<App />)
// html includes a single <style data-remix-style> with all collected CSS
```

## Performance

The DOM package is optimized for performance:

- **Small Bundle**: Minimal overhead for core functionality
- **Fast Diffing**: Optimized virtual DOM reconciliation
- **Lazy Hydration**: Support for progressive enhancement
- **Memory Efficient**: Automatic cleanup of event listeners and refs

## Testing

Components using `@remix-run/dom` can be tested with standard tools:

```tsx
import { render, flush } from '@remix-run/dom'
import { expect, test } from 'vitest'

test('button click increments counter', () => {
  let container = document.createElement('div')
  render(<Counter />, container)

  let button = container.querySelector('button')
  button.click()
  flush()

  expect(container.textContent).toContain('Count: 1')
})
```

### Inline style vs CSS-in-JS

- `style`: applies inline styles directly to the DOM element (el.style.\*). Accepts a string (serialized style attribute) or a flat object of CSS properties. Use for per-element dynamic values and CSS custom properties.
- `css`: generates a stable `rmx-*` class and injects deduped CSS (via @remix-run/style). Supports nesting, pseudo selectors, and at-rules.

Precedence: class/className < css < inline style.

SSR:

- `css` styles are collected and emitted in a single `<style data-remix-style>`.
- `style` object is serialized to the `style` attribute string for hydration. During hydration, if the attribute matches the serialized object, DOM writes are skipped to avoid thrash; subsequent updates manipulate `el.style` directly.
