# React SDK (/docs/sdk/react)

```sh
npm i @obelism/content @obelism/content-react
```

`@obelism/content-react` re-exports `editable` and the types of `@obelism/content`. Everything is a client component except `RichText`.

## Live preview [#live-preview]

```tsx title="app/layout.tsx"
import { ContentBridge } from '@obelism/content-react'

export default function RootLayout({ children }) {
	return (
		<html lang="en">
			<body>
				{/* Starts the bridge, so blocks are selectable on every page. */}
				<ContentBridge />
				{children}
			</body>
		</html>
	)
}
```

```tsx title="components/Page.tsx"
'use client'

import { editable, useLiveEntry } from '@obelism/content-react'

export const Page = ({ entry }) => {
	// The editor's unsaved version while editing; `entry` otherwise.
	const { fields } = useLiveEntry(entry)

	return (
		<main {...editable(entry)}>
			{fields.body.map((block) => (
				<section key={block._uid} {...editable(block)}>
					…
				</section>
			))}
		</main>
	)
}
```

## Reference [#reference]

- `<ContentBridge url? prompt? pagePath? />`: starts the bridge. Pass `url` only when the CMS isn't content.obelism.studio; `prompt={false}` leaves the prompt box out; `pagePath(path, locale)` maps a CMS path in the page's locale to the site's URL for pages made with New page (default `/` + the path).
- `useLiveEntry(entry, { url? })`: `entry`, or the editor's live version of it.
- `useContentBridge()`: `{ isEditing, entryId, environment }`, after hydration.
- `useContentBridgeEvent('saved' | 'published' | 'update', listener)`: e.g. `router.refresh()` on publish.
- `useContentPrompt()`: `{ available, prompting, canUndo, selectedUid, prompt, createPage, undo, select }`, for your own prompt UI with `<ContentBridge prompt={false} />`.

## Rich text [#rich-text]

```tsx
import { RichText } from '@obelism/content-react'

export const Body = ({ entry }) => (
	<RichText
		document={entry.fields.body}
		embeddedEntry={({ entry }) => <Card entry={entry} />}
		nodes={{ heading: ({ node, children }) => <h2>{children}</h2> }}
		marks={{
			link: ({ mark, children }) => (
				<Link href={mark.attrs.href}>{children}</Link>
			),
		}}
	/>
)
```

It isn't a client component, so it works in server components too. Links are only rendered for safe URLs (`http(s):`, `mailto:`, `tel:`, `#` and paths). See [rich text](/docs/api/rich-text) for the nodes and marks.
