diff --git a/docs/guides/file-system-based-automated-bundle-generation.md b/docs/guides/file-system-based-automated-bundle-generation.md index 5100ea7eaa..b6981dde3a 100644 --- a/docs/guides/file-system-based-automated-bundle-generation.md +++ b/docs/guides/file-system-based-automated-bundle-generation.md @@ -219,6 +219,276 @@ For example, if you wanted to utilize our file-system based entrypoint generatio The default value of the `auto_load_bundle` parameter can be specified by setting `config.auto_load_bundle` in `config/initializers/react_on_rails.rb` and thus removed from each call to `react_component`. +### Layout Integration with Auto-Loading + +When using `auto_load_bundle: true`, your Rails layout needs to include empty pack tag placeholders where React on Rails will inject the component-specific CSS and JavaScript bundles automatically: + +```erb + + + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + + <%= stylesheet_pack_tag %> + <%= javascript_pack_tag %> + + + <%= yield %> + + +``` + +**How it works:** + +1. **Component calls automatically append bundles**: When you use `<%= react_component("ComponentName", props, auto_load_bundle: true) %>` in a view, React on Rails automatically calls `append_javascript_pack_tag "generated/ComponentName"` and `append_stylesheet_pack_tag "generated/ComponentName"` (in static/production modes). + +2. **Layout renders appended bundles**: The empty `<%= stylesheet_pack_tag %>` and `<%= javascript_pack_tag %>` calls in your layout are where the appended component bundles get rendered. + +3. **No manual bundle management**: You don't need to manually specify which bundles to load - React on Rails handles this automatically based on which components are used in each view. + +**Example with multiple components:** + +If your view contains: +```erb +<%= react_component("HelloWorld", @hello_world_props, auto_load_bundle: true) %> +<%= react_component("HeavyMarkdownEditor", @editor_props, auto_load_bundle: true) %> +``` + +React on Rails automatically generates HTML equivalent to: +```erb + +<%= stylesheet_pack_tag "generated/HelloWorld" %> +<%= stylesheet_pack_tag "generated/HeavyMarkdownEditor" %> + + +<%= javascript_pack_tag "generated/HelloWorld" %> +<%= javascript_pack_tag "generated/HeavyMarkdownEditor" %> +``` + +This enables optimal bundle splitting where each page only loads the CSS and JavaScript needed for the components actually used on that page. + +## Complete Working Example + +Here's a step-by-step example showing how to set up file-system-based automated bundle generation from scratch: + +### 1. Configure Shakapacker + +In `config/shakapacker.yml`: + +```yml +default: &default + source_path: app/javascript + source_entry_path: packs + public_root_path: public + public_output_path: packs + nested_entries: true # Required for auto-generation + cache_manifest: false +``` + +### 2. Configure React on Rails + +In `config/initializers/react_on_rails.rb`: + +```rb +ReactOnRails.configure do |config| + config.components_subdirectory = "ror_components" # Directory name for auto-registered components + config.auto_load_bundle = true # Enable automatic bundle loading + config.server_bundle_js_file = "server-bundle.js" +end +``` + +### 3. Directory Structure + +Set up your directory structure like this: + +```text +app/javascript/ +└── src/ + ├── HelloWorld/ + │ ├── HelloWorld.module.css # Component styles + │ └── ror_components/ # Auto-registration directory + │ └── HelloWorld.jsx # React component + └── HeavyMarkdownEditor/ + ├── HeavyMarkdownEditor.module.css # Component styles + └── ror_components/ # Auto-registration directory + └── HeavyMarkdownEditor.jsx # React component +``` + +### 4. Component Implementation + +`app/javascript/src/HelloWorld/ror_components/HelloWorld.jsx`: + +```jsx +import React from 'react'; +import styles from '../HelloWorld.module.css'; + +const HelloWorld = ({ name }) => ( +
+

Hello {name}!

+

Welcome to React on Rails with auto-registration!

+
+); + +export default HelloWorld; +``` + +`app/javascript/src/HeavyMarkdownEditor/ror_components/HeavyMarkdownEditor.jsx`: + +```jsx +import React, { useState, useEffect } from 'react'; +import styles from '../HeavyMarkdownEditor.module.css'; + +const HeavyMarkdownEditor = ({ initialContent = '# Hello\n\nStart editing!' }) => { + const [content, setContent] = useState(initialContent); + const [ReactMarkdown, setReactMarkdown] = useState(null); + const [remarkGfm, setRemarkGfm] = useState(null); + + // Dynamic imports for SSR compatibility + useEffect(() => { + const loadMarkdown = async () => { + const [{ default: ReactMarkdown }, { default: remarkGfm }] = await Promise.all([ + import('react-markdown'), + import('remark-gfm') + ]); + setReactMarkdown(() => ReactMarkdown); + setRemarkGfm(() => remarkGfm); + }; + loadMarkdown(); + }, []); + + if (!ReactMarkdown) { + return
Loading editor...
; + } + + return ( +
+
+

Markdown Input:

+