From e2bfd5c7a4206c60554ad27465226f7bdeb0f0f0 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 6 Sep 2025 22:11:59 -1000 Subject: [PATCH 1/2] docs: Improve file-system-based bundle generation guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive improvements to the file-system-based automated bundle generation documentation to address gaps that cause confusion for both human developers and AI coding agents. ## Key improvements: ### 1. Layout Integration Clarification - Added detailed section explaining empty pack tag placeholders in Rails layouts - Clarified how react_component helper automatically calls append_javascript_pack_tag - Explained the connection between component usage and automatic bundle injection - Showed the complete flow from component rendering to CSS/JS loading ### 2. Complete Working Example - Added step-by-step setup guide from scratch - Included real component implementations with CSS modules and dynamic imports - Provided complete Rails controller, view, and layout code examples - Demonstrated proper directory structure with concrete file paths - Showed bundle splitting benefits with actual size comparisons (50KB vs 2.7MB) ### 3. Comprehensive Troubleshooting Section - Documented all common issues encountered during implementation - Added solutions for FOUC handling across different development modes - Covered SSR compatibility issues with dynamic imports - Included installation order requirements (Shakapacker before React on Rails) - Provided debug techniques for troubleshooting bundle loading - Addressed bundle size optimization and development vs production differences ### 4. Enhanced Developer Experience - Clear explanations of "why" behind configuration choices - Explicit file naming conventions and patterns - Debug mode instructions for investigating bundle loading issues - Common failure modes and their solutions These improvements make the documentation significantly more practical and reduce the likelihood of implementation errors. The additions are based on real-world implementation experience and address the most common stumbling blocks. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- ...ystem-based-automated-bundle-generation.md | 360 ++++++++++++++++++ 1 file changed, 360 insertions(+) diff --git a/docs/guides/file-system-based-automated-bundle-generation.md b/docs/guides/file-system-based-automated-bundle-generation.md index 5100ea7eaa..1c5ed77348 100644 --- a/docs/guides/file-system-based-automated-bundle-generation.md +++ b/docs/guides/file-system-based-automated-bundle-generation.md @@ -219,6 +219,259 @@ 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:

+