Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/oss/api-reference/view-helpers-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ Uncommonly used options:
```

- **component_name:** Can be a React component, created using a React Function Component, an ES6 class or a Render-Function that returns a React component (or, only on the server side, an object with shape `{ renderedHtml, clientProps?, redirectLocation?, routeError? }`), or a "renderer function" that manually renders a React component to the DOM (client-side only). Note, a "renderer function" is a special type of "Render-Function." A "renderer function" takes a 3rd param of a DOM ID.

> If your render function returns a hash with multiple HTML strings (e.g., `{ renderedHtml: { componentHtml, title, metaTags } }`), `react_component` raises a `ReactOnRails::Error` telling you to use [`react_component_hash`](#react_component_hash) instead. `react_component` is for rendering a single HTML result; `react_component_hash` is for rendering multiple HTML strings to place in different parts of the page.
Comment thread
ihabadham marked this conversation as resolved.
All options except `props, id, html_options` will inherit from your `react_on_rails.rb` initializer, as described [here](../configuration/README.md).

All options except `props, id, html_options` will inherit from your `react_on_rails.rb` initializer, as described in the [configuration documentation](../configuration/README.md).

- **general options:**
- **props:** Ruby Hash which contains the properties to pass to the React object, or a JSON string. If you pass a string, we'll escape it for you.
- **prerender:** enable server-side rendering of a component. Set to false when debugging!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Webpack's HMR allows the replacement of modules for React in-place without reloa

[github.com/pmmmwh/react-refresh-webpack-plugin](https://github.com/pmmmwh/react-refresh-webpack-plugin)

You can see an example commit of adding this [here](https://github.com/shakacode/react_on_rails_demo_ssr_hmr/commit/7e53803fce7034f5ecff335db1f400a5743a87e7).
You can see an [example commit adding this in the SSR HMR demo](https://github.com/shakacode/react_on_rails_demo_ssr_hmr/commit/7e53803fce7034f5ecff335db1f400a5743a87e7).

1. Add react refresh packages:
```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ We recommend committing this import statement to your version control system.

#### Background

If the `shakapacker.yml` file is configured as instructed [here](https://github.com/shakacode/shakapacker#configuration-and-code), with the following configurations
If the `shakapacker.yml` file is configured as instructed in the [Shakapacker configuration guide](https://github.com/shakacode/shakapacker#configuration-and-code), with the following configurations

```yml
default: &default
Expand Down Expand Up @@ -547,10 +547,10 @@ You don't call `registerServerComponent` yourself when using auto-bundling — t

`registerServerComponent` has two different shapes depending on which bundle you import it from:

| Bundle | Import | Signature | Why |
| --- | --- | --- | --- |
| Client | `react-on-rails-pro/registerServerComponent/client` | `registerServerComponent(...names: string[])` | Takes only component names because the server component's code stays on the server. The client fetches the RSC payload when the component renders (or uses the payload already embedded in the HTML if the page was server-rendered). |
| Server | `react-on-rails-pro/registerServerComponent/server` | `registerServerComponent(components: { [name]: Component })` | Takes an object with the actual component references because the code needs to be bundled into the server and RSC bundles for RSC payload generation. |
| Bundle | Import | Signature | Why |
| ------ | --------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Client | `react-on-rails-pro/registerServerComponent/client` | `registerServerComponent(...names: string[])` | Takes only component names because the server component's code stays on the server. The client fetches the RSC payload when the component renders (or uses the payload already embedded in the HTML if the page was server-rendered). |
| Server | `react-on-rails-pro/registerServerComponent/server` | `registerServerComponent(components: { [name]: Component })` | Takes an object with the actual component references because the code needs to be bundled into the server and RSC bundles for RSC payload generation. |

Auto-bundling uses both forms under the hood: the per-component client packs use the client form, and the aggregated server bundle file uses the server form.

Expand Down
31 changes: 16 additions & 15 deletions docs/oss/core-concepts/render-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ This guide explains how render-functions work in React on Rails and how to use t

Before diving into render-functions, it helps to know the three kinds of values you can register with `ReactOnRails.register`. React on Rails classifies each registered entry based on its shape, and the classification determines where it can run (server, client, or both) and which Ruby helpers can invoke it.

| Type | Signature | Server (SSR) | Client | Detection rule |
| --- | --- | --- | --- | --- |
| **React Component** | `(props) => JSX` or class component | Yes | Yes | `Function.length <= 1` and no `renderFunction` flag |
| **Render Function** | `(props, railsContext) => ...` | Yes | Yes | `Function.length >= 2` **or** `fn.renderFunction === true` |
| **Renderer Function** | `(props, railsContext, domNodeId) => void` | **No — throws** | Yes | A render function (detected first) with exactly `Function.length === 3` |
| Type | Signature | Server (SSR) | Client | Detection rule |
| --------------------- | ------------------------------------------ | --------------- | ------ | ----------------------------------------------------------------------- |
| **React Component** | `(props) => JSX` or class component | Yes | Yes | `Function.length <= 1` and no `renderFunction` flag |
| **Render Function** | `(props, railsContext) => ...` | Yes | Yes | `Function.length >= 2` **or** `fn.renderFunction === true` |
| **Renderer Function** | `(props, railsContext, domNodeId) => void` | **No — throws** | Yes | A render function (detected first) with exactly `Function.length === 3` |

A few important points about the detection:

Expand Down Expand Up @@ -56,15 +56,15 @@ The rest of this document focuses on **render functions** — the most flexible

The Ruby helper you use in your Rails view must be compatible with the component type you registered. Mismatches usually produce a clear server-side error, but it's faster to pick the right combination upfront:

| Component type | `react_component` | `react_component_hash` | `stream_react_component` (Pro) |
| --- | --- | --- | --- |
| **React Component** (plain function / class) | ✅ Works (client-side rendering or SSR) | ❌ Raises — the helper requires a hash return, not a component | ✅ Works (streaming SSR) |
| **Render Function returning a React component** | ✅ Works | ❌ Raises — must return a hash, not a component | ✅ Works |
| **Render Function returning `{ renderedHtml: string }`** | ✅ Works | ❌ Raises — string is not a hash with `componentHtml` | ❌ Raises — streaming does not support server render hashes |
| **Render Function returning `{ renderedHtml: ReactElement }`** | ✅ Works (calls `renderToString` on the element) | ❌ Raises — element is not a hash with `componentHtml` | ❌ Raises — streaming does not support server render hashes |
| **Render Function returning a server-render hash** (`{ renderedHtml: { componentHtml, ... } }`) | ⚠️ Raises — tells you to use `react_component_hash` | ✅ Works (the designed use case) | ❌ Raises — streaming does not support server render hashes |
| **Async Render Function** (returns a Promise) | ✅ Works with Pro Node renderer. ❌ ExecJS silently returns empty output — see [Async functions and ExecJS](#async-functions-and-execjs). | ✅ Only if the promise resolves to a server-render hash with `componentHtml`. Pro Node renderer only. | ✅ Only if the promise resolves to a React component. Promises resolving to strings or server-render hashes are rejected — streaming does not support server render hashes. |
| **Renderer Function** (3 params) | ✅ Works with `prerender: false` (client-only). ❌ Throws with `prerender: true` — renderer functions cannot run on the server. | ❌ Raises — `react_component_hash` forces `prerender: true`, which is incompatible with renderer functions | ❌ Raises — streaming requires server rendering |
| Component type | `react_component` | `react_component_hash` | `stream_react_component` (Pro) |
| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **React Component** (plain function / class) | ✅ Works (client-side rendering or SSR) | ❌ Raises — the helper requires a hash return, not a component | ✅ Works (streaming SSR) |
| **Render Function returning a React component** | ✅ Works | ❌ Raises — must return a hash, not a component | ✅ Works |
| **Render Function returning `{ renderedHtml: string }`** | ✅ Works | ❌ Raises — string is not a hash with `componentHtml` | ❌ Raises — streaming does not support server render hashes |
| **Render Function returning `{ renderedHtml: ReactElement }`** | ✅ Works (calls `renderToString` on the element) | ❌ Raises — element is not a hash with `componentHtml` | ❌ Raises — streaming does not support server render hashes |
| **Render Function returning a server-render hash** (`{ renderedHtml: { componentHtml, ... } }`) | ⚠️ Raises — tells you to use `react_component_hash` | ✅ Works (the designed use case) | ❌ Raises — streaming does not support server render hashes |
| **Async Render Function** (returns a Promise) | ✅ Works with Pro Node renderer. ❌ ExecJS silently returns empty output — see [Async functions and ExecJS](#async-functions-and-execjs). | ✅ Only if the promise resolves to a server-render hash with `componentHtml`. Pro Node renderer only. | ✅ Only if the promise resolves to a React component. Promises resolving to strings or server-render hashes are rejected — streaming does not support server render hashes. |
| **Renderer Function** (3 params) | ✅ Works with `prerender: false` (client-only). ❌ Throws with `prerender: true` — renderer functions cannot run on the server. | ❌ Raises — `react_component_hash` forces `prerender: true`, which is incompatible with renderer functions | ❌ Raises — streaming requires server rendering |

**Key takeaways:**

Expand Down Expand Up @@ -216,8 +216,9 @@ Take a look at [serverRenderReactComponent.test.ts](https://github.com/shakacode
2. **Objects Require Specific Properties** - Non-promise objects must include a `renderedHtml` property to be valid when used with `react_component`.

3. **Which object keys trigger "server render hash" processing** — React on Rails treats a returned object as a server render hash if it contains **any** of these keys: `renderedHtml`, `redirectLocation`, `routeError`, or `error`. If none of those keys are present, the object is passed through unchanged (which typically fails validation elsewhere).

> [!WARNING]
> **The `error` key is a landmine.** If your render function accidentally returns `{ error: someError }` — for example from a `try/catch` block — the framework routes it through server-render-hash handling, which produces **empty HTML output** (because `renderedHtml` is missing). Note that `hasErrors` is *not* set — only `routeError` sets the error flag, so no `PrerenderError` is raised regardless of `raise_on_prerender_error`. If you want to signal failure, throw an error instead of returning one in a plain object.
> **The `error` key is a landmine.** If your render function accidentally returns `{ error: someError }` — for example from a `try/catch` block — the framework routes it through server-render-hash handling, which produces **empty HTML output** (because `renderedHtml` is missing). Note that `hasErrors` is _not_ set — only `routeError` sets the error flag, so no `PrerenderError` is raised regardless of `raise_on_prerender_error`. If you want to signal failure, throw an error instead of returning one in a plain object.

4. **Async Functions Support Server Render Hashes** - When using the React on Rails Pro Node renderer, async render-functions can return React components, strings, or full server render hashes, including `clientProps`, `redirectLocation`, and `routeError`. See [8. Redirect Information (Legacy)](#8-redirect-information-legacy).

Expand Down
2 changes: 1 addition & 1 deletion docs/oss/core-concepts/webpack-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ A key decision in your use React on Rails is whether you go with the Shakapacker

## Option 1: Default Generator Setup: Shakapacker app/javascript

Typical Shakapacker apps have a standard directory structure as documented [here](https://github.com/shakacode/shakapacker/blob/master/README.md#configuration-and-code). If you follow [the basic tutorial](../getting-started/tutorial.md), you will see this pattern in action. In order to customize the Webpack configuration, consult the [Shakapacker webpack customization docs](https://github.com/shakacode/shakapacker#webpack-configuration).
Typical Shakapacker apps have a standard directory structure as documented in the [Shakapacker README](https://github.com/shakacode/shakapacker/blob/master/README.md#configuration-and-code). If you follow [the basic tutorial](../getting-started/tutorial.md), you will see this pattern in action. In order to customize the Webpack configuration, consult the [Shakapacker webpack customization docs](https://github.com/shakacode/shakapacker#webpack-configuration).

The _advantage_ of using Shakapacker to configure Webpack is that there is very little code needed to get started, and you don't need to understand really anything about Webpack customization.

Expand Down
8 changes: 4 additions & 4 deletions docs/oss/migrating/migrating-to-rsc.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ Before diving into the React patterns, understand how RSC maps to React on Rails

**Three API changes per component.** Each component you migrate touches three layers:

| Layer | Before | After |
| --------------- | ------------------------------------ | ------------------------------------------------------------- |
| ERB view helper | `react_component("Product", ...)` | `stream_react_component("Product", ...)` |
| Layer | Before | After |
| --------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ERB view helper | `react_component("Product", ...)` | `stream_react_component("Product", ...)` |
| JS registration | `ReactOnRails.register({ Product })` | `registerServerComponent` (signature varies per bundle — see [details](../core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md#the-two-registerservercomponent-signatures)) |
| Controller | Standard Rails controller | Add `include ReactOnRailsPro::Stream` |
| Controller | Standard Rails controller | Add `include ReactOnRailsPro::Stream` |

**Three webpack bundles.** RSC requires separate client, server, and RSC bundles. The `registerServerComponent` API behaves differently in each:

Expand Down
Loading
Loading