From 10bcfae12c772c02b44a004525a500ac6ac7fc8b Mon Sep 17 00:00:00 2001 From: ihabadham Date: Wed, 15 Apr 2026 21:53:05 +0200 Subject: [PATCH 1/3] chore: apply prettier formatting to docs Restores prettier compliance on five tracked docs files that landed on main via a docs-only commit. The lint workflow is conditionally skipped for docs-only commits on main (see .github/workflows/lint-js-and-ruby.yml job condition), so the formatting drift was never caught on merge. It surfaces on every subsequent code-touching PR because the lint job runs for them. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/oss/api-reference/view-helpers-api.md | 2 +- ...ystem-based-automated-bundle-generation.md | 8 ++--- docs/oss/core-concepts/render-functions.md | 31 ++++++++++--------- docs/oss/migrating/migrating-to-rsc.md | 8 ++--- .../inside-client-components.md | 27 +++++++--------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/docs/oss/api-reference/view-helpers-api.md b/docs/oss/api-reference/view-helpers-api.md index e350028dd0..7215f66646 100644 --- a/docs/oss/api-reference/view-helpers-api.md +++ b/docs/oss/api-reference/view-helpers-api.md @@ -26,7 +26,7 @@ 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. - 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 [here](../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! diff --git a/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md b/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md index a30f8687c5..f17af7baf1 100644 --- a/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md +++ b/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md @@ -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. diff --git a/docs/oss/core-concepts/render-functions.md b/docs/oss/core-concepts/render-functions.md index 8cc3574329..145bc53723 100644 --- a/docs/oss/core-concepts/render-functions.md +++ b/docs/oss/core-concepts/render-functions.md @@ -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: @@ -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:** @@ -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). diff --git a/docs/oss/migrating/migrating-to-rsc.md b/docs/oss/migrating/migrating-to-rsc.md index 3960b60178..25465aa657 100644 --- a/docs/oss/migrating/migrating-to-rsc.md +++ b/docs/oss/migrating/migrating-to-rsc.md @@ -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: diff --git a/docs/pro/react-server-components/inside-client-components.md b/docs/pro/react-server-components/inside-client-components.md index f41410ebaa..347d9b6b07 100644 --- a/docs/pro/react-server-components/inside-client-components.md +++ b/docs/pro/react-server-components/inside-client-components.md @@ -73,10 +73,7 @@ export default function AppRouter({ userId }) { path="/dashboard" element={} /> - } - /> + } /> ); @@ -396,9 +393,7 @@ export default function DetailsPanel({ id }) { const [isOpen, setIsOpen] = useState(false); return (
- + {isOpen && ( Loading details…
}> @@ -413,15 +408,15 @@ export default function DetailsPanel({ id }) { Unless noted otherwise, each API below is a default export — use default-import syntax. -| API | Import | Export type | Purpose | -| --- | --- | --- | --- | -| `RSCRoute` | `react-on-rails-pro/RSCRoute` | Default | Renders a server component inside a client component. Props: `componentName: string`, `componentProps: object`. | -| `wrapServerComponentRenderer` (client) | `react-on-rails-pro/wrapServerComponentRenderer/client` | Default | Wraps a `'use client'` component for client-side hydration. Provides the context `RSCRoute` needs internally. The wrapped result must be registered with `ReactOnRails.register` unless you use auto-bundling. | -| `wrapServerComponentRenderer` (server) | `react-on-rails-pro/wrapServerComponentRenderer/server` | Default | Same as above, for server-side rendering. The wrapped function receives `railsContext` as its second argument. | -| `registerServerComponent` (client) | `react-on-rails-pro/registerServerComponent/client` | Default | Registers server component placeholders in the client bundle. Takes names as strings: `registerServerComponent('A', 'B')`. The client fetches the RSC payload from the server or uses the payload already embedded in the HTML. | -| `registerServerComponent` (server) | `react-on-rails-pro/registerServerComponent/server` | Default | Registers server components in the server bundle. Takes an object: `registerServerComponent({ A, B })`. | -| `useRSC` | `import { useRSC } from 'react-on-rails-pro/RSCProvider'` | **Named** | Hook providing `refetchComponent(name, props)` for manual refetch and error recovery. Available anywhere inside a tree set up by `wrapServerComponentRenderer` or `registerServerComponent`. | -| `isServerComponentFetchError` | `import { isServerComponentFetchError } from 'react-on-rails-pro/ServerComponentFetchError'` | **Named** | Type guard to check if an error came from a failed server component fetch. The error has `serverComponentName` and `serverComponentProps` fields. | +| API | Import | Export type | Purpose | +| -------------------------------------- | -------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `RSCRoute` | `react-on-rails-pro/RSCRoute` | Default | Renders a server component inside a client component. Props: `componentName: string`, `componentProps: object`. | +| `wrapServerComponentRenderer` (client) | `react-on-rails-pro/wrapServerComponentRenderer/client` | Default | Wraps a `'use client'` component for client-side hydration. Provides the context `RSCRoute` needs internally. The wrapped result must be registered with `ReactOnRails.register` unless you use auto-bundling. | +| `wrapServerComponentRenderer` (server) | `react-on-rails-pro/wrapServerComponentRenderer/server` | Default | Same as above, for server-side rendering. The wrapped function receives `railsContext` as its second argument. | +| `registerServerComponent` (client) | `react-on-rails-pro/registerServerComponent/client` | Default | Registers server component placeholders in the client bundle. Takes names as strings: `registerServerComponent('A', 'B')`. The client fetches the RSC payload from the server or uses the payload already embedded in the HTML. | +| `registerServerComponent` (server) | `react-on-rails-pro/registerServerComponent/server` | Default | Registers server components in the server bundle. Takes an object: `registerServerComponent({ A, B })`. | +| `useRSC` | `import { useRSC } from 'react-on-rails-pro/RSCProvider'` | **Named** | Hook providing `refetchComponent(name, props)` for manual refetch and error recovery. Available anywhere inside a tree set up by `wrapServerComponentRenderer` or `registerServerComponent`. | +| `isServerComponentFetchError` | `import { isServerComponentFetchError } from 'react-on-rails-pro/ServerComponentFetchError'` | **Named** | Type guard to check if an error came from a failed server component fetch. The error has `serverComponentName` and `serverComponentProps` fields. | ## Troubleshooting From 1330da587dc29e436eb7caf3daad5703b64c7910 Mon Sep 17 00:00:00 2001 From: ihabadham Date: Wed, 15 Apr 2026 21:59:35 +0200 Subject: [PATCH 2/3] Preserve blockquote/paragraph boundary on view-helpers-api note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prettier was normalizing adjacent indented lines inside the same list item by propagating the blockquote prefix to a plain-text continuation. Insert a blank line between them so the source unambiguously expresses two separate blocks — blockquote (react_component_hash warning) and plain paragraph (initializer inheritance note). Keeps the rendered output aligned with the original intent and keeps this PR truly whitespace-only. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/oss/api-reference/view-helpers-api.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/oss/api-reference/view-helpers-api.md b/docs/oss/api-reference/view-helpers-api.md index 7215f66646..46847eb00b 100644 --- a/docs/oss/api-reference/view-helpers-api.md +++ b/docs/oss/api-reference/view-helpers-api.md @@ -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. - > 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 [here](../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! From d22f06da851626b7b1c4b715d3a9e1d64d0fbbfe Mon Sep 17 00:00:00 2001 From: ihabadham Date: Wed, 15 Apr 2026 22:06:49 +0200 Subject: [PATCH 3/3] Replace vague [here] link text with descriptive phrases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligns four legacy outliers with the docs directory's dominant link-text convention (1216 of 1220 links already use descriptive text). Also satisfies markdownlint MD059 on the line flagged during review. Files touched: - docs/oss/api-reference/view-helpers-api.md - docs/oss/building-features/rails-webpacker-react-integration-options.md - docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md - docs/oss/core-concepts/webpack-configuration.md Link targets unchanged — only the visible link text is updated. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/oss/api-reference/view-helpers-api.md | 2 +- .../rails-webpacker-react-integration-options.md | 2 +- ...to-bundling-file-system-based-automated-bundle-generation.md | 2 +- docs/oss/core-concepts/webpack-configuration.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/oss/api-reference/view-helpers-api.md b/docs/oss/api-reference/view-helpers-api.md index 46847eb00b..2ccb0a5823 100644 --- a/docs/oss/api-reference/view-helpers-api.md +++ b/docs/oss/api-reference/view-helpers-api.md @@ -28,7 +28,7 @@ Uncommonly used options: > 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. - 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. diff --git a/docs/oss/building-features/rails-webpacker-react-integration-options.md b/docs/oss/building-features/rails-webpacker-react-integration-options.md index d4fd37722c..3a64ca2bbe 100644 --- a/docs/oss/building-features/rails-webpacker-react-integration-options.md +++ b/docs/oss/building-features/rails-webpacker-react-integration-options.md @@ -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 diff --git a/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md b/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md index f17af7baf1..fba0aa05de 100644 --- a/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md +++ b/docs/oss/core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md @@ -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 diff --git a/docs/oss/core-concepts/webpack-configuration.md b/docs/oss/core-concepts/webpack-configuration.md index 4129111727..135e80a5b9 100644 --- a/docs/oss/core-concepts/webpack-configuration.md +++ b/docs/oss/core-concepts/webpack-configuration.md @@ -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.