> ## Documentation Index
> Fetch the complete documentation index at: https://juo.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> React 18+ adapter for rendering blocks as components and reading reactive state with useSignal.

`@juo/blocks/react` is the React 18+ adapter. It exposes `createReactRenderer` and a set of hooks for context and signal interop.

```sh theme={null}
pnpm add react react-dom
```

The patterns below extend the [Blocks](/docs/blocks/concepts/blocks), [Reactivity](/docs/blocks/concepts/reactivity), and [Contexts](/docs/blocks/concepts/contexts) concept pages with React-specific equivalents.

## Defining a React block

```tsx theme={null}
// Banner/index.ts
import { defineBlock, registerBlock } from "@juo/blocks";
import { createReactRenderer } from "@juo/blocks/react";
import { Banner } from "./Banner";

export const BannerBlock = defineBlock("Banner", {
  group: "theme",
  schema: /* ... */,
  initialValue: () => ({ props: { tone: "info", message: "Welcome back" } }),
  renderer: createReactRenderer(Banner),
});

registerBlock(BannerBlock);
```

```tsx theme={null}
// Banner/Banner.tsx
export function Banner({ tone, message }: { tone: string; message: string }) {
  return <div className={`banner banner--${tone}`}>{message}</div>;
}
```

`createReactRenderer` mounts the component inside a `ContextProvider` that resolves context from the surrounding `<juo-context-root>`. Hooks inside the tree can then call `useContext` and `useSignal`.

## Reading services with `useContext`

`useContext` (the one from `@juo/blocks/react`, not React's own) returns a proxy where every `Signal<T>` becomes a `[value, setValue]` tuple driven by `useSyncExternalStore`. Components re-render when accessed fields change:

```tsx theme={null}
import { useContext } from "@juo/blocks/react";
import { SubscriptionServiceContext } from "@juo/blocks";

export function SubscriptionStatus() {
  const subscription = useContext(SubscriptionServiceContext);

  // subscription.current is [Subscription | null, setter]
  const [current] = subscription.current;
  const status = current?.status ?? "unknown";

  return (
    <div>
      <p>Status: {status}</p>
      <button onClick={() => current && subscription.pause(current.id)}>
        Pause
      </button>
    </div>
  );
}
```

The `$` accessor returns the raw service when direct signal access is required:

```tsx theme={null}
const subscription = useContext(SubscriptionServiceContext);
const rawSignal = subscription.$.current; // Signal<Subscription | null>
```

## `useSignal` — read any signal as React state

For signals that aren't part of a context (a service-internal signal, a `computed`, an externally created one), `useSignal` bridges to React state:

```tsx theme={null}
import { useSignal } from "@juo/blocks/react";
import { signal, computed } from "@juo/blocks";

const counter = signal(0);
const doubled = computed(() => counter.value * 2);

export function Counter() {
  const value = useSignal(counter);
  const x2 = useSignal(doubled);

  return (
    <button onClick={() => (counter.value = value + 1)}>
      {value} (×2 = {x2})
    </button>
  );
}
```

## Providing contexts from React

Use `useProvideContext` inside a wrapper component:

```tsx theme={null}
import { useProvideContext } from "@juo/blocks/react";
import { CartContext, createCart } from "./cart";

export function CartProvider({ children }: { children: React.ReactNode }) {
  useProvideContext(CartContext, createCart());
  return <>{children}</>;
}
```
