docs/oss/getting-started/project-structure.md
React on Rails supports two main organizational approaches for your React components.
The current React on Rails generator creates a component-based structure optimized for automatic bundle generation:
app/javascript/
├── src/
│ ├── HelloWorld/
│ │ ├── HelloWorld.module.css
│ │ └── ror_components/ # Auto-discovered by React on Rails
│ │ ├── HelloWorld.jsx # Client & server rendering
│ │ └── HelloWorld.server.js # Optional: server-only code
│ └── AnotherComponent/
│ └── ror_components/
│ ├── AnotherComponent.client.jsx # Client-only rendering
│ └── AnotherComponent.server.jsx # Server-only rendering
└── packs/
├── generated/ # Auto-generated entry points (gitignored)
│ ├── HelloWorld.js
│ └── AnotherComponent.js
└── server-bundle.js # Server rendering entry point
Key features:
ror_components/ directories are automatically discovered and registeredReactOnRails.register() calls needed.client.jsx and .server.jsx files for different rendering logic (these control client versus server bundle placement, not React Server Components)For details, see Auto-Bundling Guide and Generator Details.
For projects requiring explicit control over webpack entry points:
app/javascript/
├── bundles/
│ └── HelloWorld/
│ ├── components/
│ │ └── HelloWorld.jsx
│ └── startup/
│ └── registration.js # Manual ReactOnRails.register()
└── packs/
└── hello-world-bundle.js # Webpack entry point
This approach requires manual component registration and webpack configuration but offers complete control over bundling strategy.
Use modern auto-bundling if:
Use traditional manual structure if:
For most projects, we recommend the modern auto-bundling approach.
/client directory for source codemv app/javascript client
/config/shakapacker.yml file. Change the default/source_path:source_path: client
React on Rails supports multiple approaches for styling your components. The modern recommended approach uses CSS Modules with co-located stylesheets.
The generator creates components with CSS Module support out of the box:
app/javascript/src/HelloWorld/
├── ror_components/
│ ├── HelloWorld.client.jsx
│ └── HelloWorld.module.css # Co-located with component
Example usage:
import React from 'react';
import * as style from './HelloWorld.module.css';
const HelloWorld = () => <label className={style.bright}>Hello World</label>;
Benefits:
You can continue using Rails' traditional asset pipeline with sass-rails or similar gems:
<%# app/views/layouts/application.html.erb %>
<%= stylesheet_link_tag 'application', media: 'all' %>
Use this approach when:
For global styles (fonts, resets, variables), you can create additional webpack entry points:
app/javascript/
├── packs/
│ ├── application.css # Global styles
│ └── server-bundle.js
└── src/
└── HelloWorld/
└── ror_components/
├── HelloWorld.jsx
└── HelloWorld.module.css
Import global styles in your layout:
<%= stylesheet_pack_tag 'application' %>