Skip to content

Latest commit

 

History

History
379 lines (300 loc) · 12.6 KB

File metadata and controls

379 lines (300 loc) · 12.6 KB

React on Rails Quick Start - Rails 8 + React + Shakapacker v15 Auto-Registration

This documentation shows how to create a React on Rails application using React on Rails v15 auto-registration with the corrected installation sequence that fixes the "package.json not found" error.

📍 Working Demo: https://github.com/shakacode/test-react-on-rails-v15-hello-world-on-rails-v8

Overview

This sample app demonstrates:

  • Auto-registration feature: Zero manual ReactOnRails.register() calls needed
  • Bundle splitting: Lightweight (~50KB) vs Heavy (~2.7MB) components
  • File-system-based bundle generation: Components auto-detected from directory structure
  • Correct installation order: Shakapacker first, then React on Rails
  • Rails 8.0.1 with React 19.1.1 and React on Rails 15.0.0
  • Shakapacker 8.3.0 with nested_entries: true
  • Server-side rendering (SSR) with prerender: true
  • Modern React patterns with hooks, CSS modules, and dynamic imports
  • Git commit history documenting each step

Critical Installation Sequence

⚠️ Important: Install Shakapacker BEFORE React on Rails to avoid package.json errors.

The Correct Sequence

# 1. Create new Rails 8 app
rails new my_app --skip-jbuilder
cd my_app

# 2. Add Shakapacker FIRST (this creates package.json)
bundle add shakapacker
bundle exec rails generate shakapacker:install

# 3. Then add React on Rails (now package.json exists)
bundle add react-on-rails
bundle exec rails generate react_on_rails:install

# 4. Install dependencies with npm
npm install

# 5. Enable auto-registration in config/initializers/react_on_rails.rb
# Add these lines:
# config.components_subdirectory = "ror_components"  
# config.auto_load_bundle = true
# config.server_bundle_js_file = "server-bundle.js"

# 6. Create components using directory structure (no generators needed!)
mkdir -p app/javascript/src/HelloWorld/ror_components
mkdir -p app/javascript/src/HeavyMarkdownEditor/ror_components

# 7. Generate webpack entries for auto-registration
rake react_on_rails:generate_packs

# 8. Run development server
bin/dev

Why This Order Matters

  • Shakapacker creates package.json and sets up the JavaScript build pipeline
  • React on Rails generator expects package.json to exist when adding React dependencies
  • Wrong order causes: ERROR: package.json not found

Auto-Registration File Structure

React on Rails v15 auto-registration uses a file-system-based approach where components are automatically detected and registered:

app/
├── controllers/
│   ├── hello_world_controller.rb
│   └── heavy_markdown_editor_controller.rb
│   └── heavy_markdown_editor_content.md     # Content file (simulates database)
├── javascript/
│   ├── src/                                # Auto-registration source directory
│   │   ├── HelloWorld/                     # Component name (directory)
│   │   │   └── ror_components/             # Magic directory for auto-detection
│   │   │       ├── HelloWorld.jsx          # Component implementation  
│   │   │       └── HelloWorld.module.css   # CSS modules styling
│   │   └── HeavyMarkdownEditor/            # Second component
│   │       └── ror_components/
│   │           ├── HeavyMarkdownEditor.jsx
│   │           └── HeavyMarkdownEditor.module.css
│   ├── generated/                          # Auto-generated webpack entries
│   │   ├── HelloWorld.js                   # Generated by rake task
│   │   ├── HeavyMarkdownEditor.js          # Generated by rake task  
│   │   └── server-bundle-generated.js      # Generated server bundle
│   └── packs/
│       ├── application.js                  # Main application entry
│       └── server-bundle.js                # Server rendering entry
└── views/
    ├── hello_world/
    │   └── index.html.erb
    ├── heavy_markdown_editor/
    │   └── index.html.erb
    └── layouts/
        └── application.html.erb            # Unified layout (no component-specific)

Key Auto-Registration Concepts:

  1. Magic Directory: app/javascript/src/ComponentName/ror_components/
  2. Auto-Detection: rake react_on_rails:generate_packs scans these directories
  3. Generated Entries: Webpack entries created in app/javascript/generated/
  4. No Manual Registration: Components automatically available to Rails views
  5. Bundle Splitting: Each component gets its own webpack bundle

Component Auto-Registration Demo

This app demonstrates React on Rails' auto-registration feature with bundle splitting:

HelloWorld Component (/ and /hello_world)

  • Lightweight bundle (~50KB) with React basics
  • Interactive name input with React state
  • Navigation link to HeavyMarkdownEditor
  • CSS modules for styling
  • Server-side rendering enabled
  • Demonstrates fast-loading components

HeavyMarkdownEditor Component (/heavy_markdown_editor)

  • Heavy bundle (~2.7MB) with 58+ dependencies including:
    • ReactMarkdown for parsing
    • Remark GFM for GitHub-flavored markdown
    • Dynamic imports to prevent SSR issues
  • Live markdown editor with preview
  • Skeleton loader to prevent FOUC
  • Content loaded from Rails controller (simulating database)
  • Demonstrates bundle splitting benefits

Auto-Generated Bundle Configuration

React on Rails v15 automatically generates webpack entries. No manual registration needed!

generated/HelloWorld.js (auto-generated):

import ReactOnRails from 'react-on-rails';
import * as HelloWorldModule from '../src/HelloWorld/ror_components/HelloWorld.module.css';
import HelloWorld from '../src/HelloWorld/ror_components/HelloWorld';

ReactOnRails.register({
  HelloWorld,
});

generated/HeavyMarkdownEditor.js (auto-generated):

import ReactOnRails from 'react-on-rails';
import * as HeavyMarkdownEditorModule from '../src/HeavyMarkdownEditor/ror_components/HeavyMarkdownEditor.module.css';
import HeavyMarkdownEditor from '../src/HeavyMarkdownEditor/ror_components/HeavyMarkdownEditor';

ReactOnRails.register({
  HeavyMarkdownEditor,
});

⚠️ Important: These files are generated by rake react_on_rails:generate_packs. Don't edit them manually!

Rails Integration

Controller Pattern

class HeavyMarkdownEditorController < ApplicationController
  def index
    @heavy_markdown_editor_props = {
      initialText: load_demo_content,  # Loads from .md file (simulates database)
      title: "React on Rails Demo Article",
      author: "Demo System", 
      lastModified: Time.current
    }
  end

  private

  def load_demo_content
    # In production: Article.find(id).content
    content_file = File.join(__dir__, 'heavy_markdown_editor_content.md')
    File.read(content_file)
  rescue Errno::ENOENT => e
    "# Demo content not available"
  end
end

View Pattern with SSR

<%= react_component("HeavyMarkdownEditor", 
                   props: @heavy_markdown_editor_props, 
                   prerender: true) %>

Unified Application Layout

<!DOCTYPE html>
<html>
  <head>
    <title>React on Rails v15 Auto-Registration</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Key Change: With auto-registration, the layout doesn't need component-specific javascript_pack_tag calls. The generated bundles are automatically loaded when components are rendered.

Routes Configuration

Rails.application.routes.draw do
  root "hello_world#index"
  get 'hello_world', to: 'hello_world#index'  
  get 'second_component', to: 'second_component#index'
end

Modern React Patterns

Functional Components with Hooks

import React, { useState } from 'react';
import styles from './HelloWorld.module.css';

const HelloWorld = (props) => {
  const [name, setName] = useState(props.name);
  
  return (
    <div>
      <h3>Hello, {name}!</h3>
      <input 
        type="text" 
        value={name} 
        onChange={(e) => setName(e.target.value)} 
      />
    </div>
  );
};

export default HelloWorld;

CSS Modules

/* HelloWorld.module.css */
.bright {
  color: red;
  font-weight: bold;
}

Key Concepts Demonstrated

  1. Proper Installation Sequence: Shakapacker → React on Rails
  2. Component Auto-Registration: No manual ReactOnRails.register calls needed
  3. Bundle Separation: Each component has its own JavaScript pack
  4. Modern React: Hooks, functional components, CSS modules
  5. Rails Integration: Controllers, views, layouts working together
  6. Navigation: Component-to-component routing via Rails routes

Running the Application

Development Mode (with HMR)

# Development server with Hot Module Replacement
bin/dev

# Access the application
open http://localhost:3000

Testing Development and Production Builds

We've enhanced the bin/dev script to support three different modes:

Development Mode (default - with HMR and FOUC):

bin/dev
open http://localhost:3000

Static Development Mode (no HMR, no FOUC):

# Development environment with extracted CSS - best of both worlds
bin/dev static
open http://localhost:3000

Production-Assets Mode (optimized bundles, no FOUC):

# Enhanced script automatically precompiles and serves production assets
bin/dev prod
open http://localhost:3001

# Or use the full command name
bin/dev production-assets

Help:

bin/dev help

Mode Comparison

Aspect Development (HMR) Static Development Production-Assets
FOUC ❌ May occur during HMR ✅ No FOUC ✅ No FOUC
HMR ✅ Live reloading ❌ Manual refresh ❌ Manual refresh
CSS Loading CSS modules async Extracted files Extracted + minified
Bundle Size Full + source maps Full + source maps Minified + optimized
Build Speed Fastest Fast Slowest
Environment Development Development Production
Port 3000 3000 3001
Use Case Active development Testing without FOUC Pre-deploy testing

💡 Recommendation: Use bin/dev static when you need to test the UI without FOUC but still want development speed and source maps.

Cleaning Up After Production Testing

# Remove production assets to return to development mode
rm -rf public/packs

# Restart development server
bin/dev

Git Commit History

This repository's commits document each step:

  1. Initial commit: Rails 8 + React on Rails 15.0 + Shakapacker 8.3 hello world app
  2. Switch to npm: Remove yarn lockfile, use npm as default
  3. Root redirect: Add root path redirect to hello_world
  4. Second component: Create SecondComponent to demonstrate auto-registration
  5. Navigation: Add navigation link between components

Testing the Fix

You can verify the installation sequence works by:

  1. Following this exact sequence on a fresh Rails 8 app
  2. Confirming no "package.json not found" errors occur
  3. Running the app and seeing both components work
  4. Checking that auto-registration loads components properly

Common Issues & Solutions

"package.json not found" Error

  • Cause: Installing React on Rails before Shakapacker
  • Solution: Install Shakapacker first to create package.json

Component Not Loading

  • Check: Ensure bundle is included in layout via javascript_pack_tag
  • Check: Component is registered in the corresponding pack file
  • Check: Routes are configured correctly

Webpack Compilation Errors

  • Check: package-lock.json exists (not yarn.lock)
  • Check: All dependencies are installed via npm install
  • Check: Babel configuration is present in package.json

AI Agent Instructions

To recreate this app, an AI agent should:

  1. Follow the exact installation sequence documented here
  2. Use npm as the default package manager
  3. Create separate bundles for each component to demonstrate isolation
  4. Include navigation between components to show routing
  5. Add proper styling to make the demo visually appealing
  6. Document each step with meaningful git commits
  7. Test the final result to ensure everything works

This approach creates a robust sample that demonstrates both the technical fix and best practices for React on Rails development.