npm package: @nym.sh/jrx
Description: JSX factory for json-render. Write JSX, get Spec JSON.
Latest version: 0.2.0

# @nym.sh/jrx

JSX factory for [json-render](https://github.com/vercel-labs/json-render). Write JSX, get Spec JSON.

## Install

```bash
bun add @nym.sh/jrx @json-render/core
```

## Setup

Configure your `tsconfig.json` to use @nym.sh/jrx as the JSX source:

```json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@nym.sh/jrx"
  }
}
```

Or use a per-file pragma:

```tsx
/** @jsxImportSource @nym.sh/jrx */
```

## Usage

### Define components

Create wrapper functions that map JSX tags to json-render component type names:

```ts
import { jsx } from "@nym.sh/jrx/jsx-runtime";
import type { JrxNode } from "@nym.sh/jrx";

export function Stack(props: Record<string, unknown>): JrxNode {
  return jsx("Stack", props);
}

export function Text(props: Record<string, unknown>): JrxNode {
  return jsx("Text", props);
}

export function Button(props: Record<string, unknown>): JrxNode {
  return jsx("Button", props);
}
```

### Render JSX to Spec JSON

```tsx
import { render } from "@nym.sh/jrx";
import { Stack, Text, Button } from "./components";

const spec = render(
  <Stack>
    <Text content="Hello from jrx!" />
    <Button label="Click me" />
  </Stack>
);
```

This produces:

```json
{
  "root": "stack-1",
  "elements": {
    "text-1": {
      "type": "Text",
      "props": { "content": "Hello from jrx!" }
    },
    "button-1": {
      "type": "Button",
      "props": { "label": "Click me" }
    },
    "stack-1": {
      "type": "Stack",
      "props": {},
      "children": ["text-1", "button-1"]
    }
  }
}
```

### State, events, visibility, and watchers

Pass json-render bindings as JSX props:

```tsx
const spec = render(
  <Stack>
    <Text content={{ $state: "/count" }} />
    <Button
      label="Increment"
      on={{
        press: {
          action: "increment",
          params: { statePath: "/count" },
        },
      }}
    />
    <Text
      content="Hidden until toggled"
      visible={{ $state: "/showDetails", eq: true }}
    />
  </Stack>,
  {
    state: {
      count: 0,
      showDetails: false,
    },
  }
);
```

The `render()` function accepts an options object with `state` to include initial state in the Spec output.

### Reserved props

These props are extracted from JSX and mapped to Spec fields rather than passed through as component props:

| Prop | Spec field | Description |
|---|---|---|
| `key` | element key | Explicit element key (overrides auto-generation) |
| `visible` | `visible` | Visibility condition |
| `on` | `on` | Event bindings |
| `repeat` | `repeat` | Repeat configuration |
| `watch` | `watch` | State watchers |
| `children` | `children` | Child element references |

## Example

The `example/` directory contains a Bun HTTP server that demonstrates jrx in action. It shows JSX source, live rendered UI (via `@json-render/react`), and JSON output side by side.

```bash
cd example
bun install
bun dev
```

This starts a dev server with HMR at `http://localhost:3000`.

## Development

```bash
bun install       # install dependencies
bun run build     # build dist/
bun test          # run tests
bun run typecheck # type check
```

## License

MIT