React on Rails Migration Examples: Meta Tracking Issue
This issue tracks open-source Rails applications that are strong candidates for incremental migration to react_on_rails, along with the public PRs, blockers, benchmark notes, and follow-up work.
Public docs should only carry durable example references. This issue is the better home for in-progress third-party PRs, maintainer-contact notes, and other working material that may change quickly.
The goal is not to rewrite large apps wholesale. The goal is to prove a repeatable migration path from existing Rails-plus-React stacks such as react-rails, webpacker, shakapacker, vite_rails, and custom Rails React bridges.
Why This Matters
Teams evaluating react_on_rails want proof from real applications, not only toy examples.
The most credible evidence is:
- Real open-source Rails repos
- Narrow migration PRs
- Reproducible before-and-after notes
- Performance or maintainability evidence for each migrated slice
Selection Policy
- Prefer very popular or institutionally credible Rails applications.
- Favor apps already using React with Rails in some form.
- Keep the first PR small enough to review quickly.
- Avoid obscure dependencies or novelty migrations that do not help the main adoption story.
- Treat Inertia-first apps as stretch targets unless the point is to document architecture differences.
First-Wave Status
| Repo |
Stars |
Current Integration |
Status |
PR |
Evidence so far |
Missing proof |
EFForg/action-center-platform |
517 |
react-rails |
PR ready for review |
#975 |
Contributor-local benchmark note captured: warm request time and total JS weight both decreased for the slice; local maintainability note is captured for the new explicit Rails prop boundary and the narrowed page-scoped React on Rails entrypoint; local webpack compilation passes, the repo Docker build now passes with the Node 20 + Yarn-aligned container fix, and compose-backed db:test:prepare plus the focused request/helper specs pass |
Optional browser-level proof if a maintainer asks for stronger route evidence; otherwise the next step is maintainer review |
thewca/worldcubeassociation.org |
383 |
react-rails |
PR ready for review |
#14010 |
Local maintainability note captured for the legacy disclaimer page; lint, RuboCop, webpack compile, repo bootstrap via docker compose up --build wca_environment, and the compose-backed disclaimer request spec now pass after the branch restores legacy react_component(...) calls through a coexistence override |
Optional CI confirmation or one more low-risk legacy Rails slice if maintainers want broader proof; otherwise the next step is maintainer review |
demarche-numerique/demarche.numerique.gouv.fr |
259 |
vite_rails + custom React bridge |
Closed after maintainer feedback |
#12954 |
Focused component spec passed; local maintainability note captured for replacing a custom coldwired/react mount contract with a maintained helper contract |
External blocker: maintainer said the repo depends on coldwired updating React components during DOM morphs, so this migration slice is not a fit for their architecture |
GSA/search-gov |
68 |
react-rails + Shakapacker |
PR ready for review |
#2010 |
CircleCI green for checkout code, jest, rspec, and cucumber; local maintainability note captured for splitting low-coupling SERP chrome into smaller mounts; PR rationale already explains the maintainability-first value of shrinking the shell boundary without rewriting the results body |
Optional public-friendly benchmark note if stronger marketing proof is needed; otherwise the next step is maintainer review |
hackclub/hcb |
845 |
react-rails |
PR ready for review |
#13489 |
Repo Dockerfile builds successfully, compose-backed db:test:prepare plus the focused bookkeeping controller spec now pass, containerized yarn build, yarn build:css, and focused RuboCop revalidation all pass once the app uses an explicit React-on-Rails alias plus a legacy-helper coexistence override; local maintainability note is now captured for that coexistence path; asset-level comparison shows bundle.js moved from 2,739,714 to 2,754,760 bytes while yarn build improved from 10,035 ms to 9,101 ms |
Optional browser-level pass if stronger public performance proof is needed; otherwise the next step is maintainer review |
broadinstitute/single_cell_portal_core |
70 |
vite_rails + custom window.SCP.renderComponent bridge |
Draft PR open |
#2381 |
Local helper-test, focused Jest, and vite build --force --clear all pass for the studies#usage_stats slice; asset-level comparison shows application-*.js moved from 2,083,391 to 2,099,314 bytes while full rebuild time moved from 22,947 ms to 23,486 ms |
Controller/request-level HTML proof once Mongo-backed test auth is available |
Second-Wave Targets
These matter, but they are weaker first proof points than the current wave.
| Repo |
Stars |
Current Integration |
Current status |
thecartercenter/nemo |
65 |
react-rails + shakapacker |
PR ready for review: #1110. Fresh local db:setup, compile, react_on_rails:doctor, request spec, system spec, RuboCop, and focused JS lint all pass; shared global components chunk dropped from 305,252 to 293,091 bytes |
Codeminer42/cm42-central |
324 |
vite_rails + React |
Draft PR open: #1029. Local Docker/Linux validation passes: db:setup, focused controller spec, focused JS specs, ESLint, and vite build all succeed; the built application-*.js bundle moved from 2,193,537 to 2,209,328 bytes |
broadinstitute/single_cell_portal_core |
70 |
vite_rails + custom window.SCP.renderComponent bridge |
Draft PR open: #2381. Local helper-test, focused Jest, and vite build --force --clear pass; the built application-*.js bundle moved from 2,083,391 to 2,099,314 bytes |
indentlabs/notebook |
398 |
react-rails + Webpacker (ReactRailsUJS auto-mount) |
Compile still passes locally, but a real landing-page request now fails because the migrated helper expects config/shakapacker.yml; the branch is therefore blocked on whether to add real Shakapacker config or stage a broader migration first |
saeloun/miru-web |
256 |
vite_rails + SPA root |
Local Ruby and package installs pass, but the router still falls through home#index into one global Vite entrypoint that mounts a single #react-root, so this is an app-shell case study rather than an incremental island migration |
codeforpdx/dwellingly-app |
49 |
react-rails with SPA routing |
Local work is blocked by the pinned Ruby 3.0.1 not building on this macOS arm64 host |
rubyforgood/demand-progress |
56 |
Webpacker + ad hoc React mount |
Local work is blocked by the locked Ruby 2.5.1 not building on this macOS arm64 host |
Stretch and Blocked Targets
| Repo |
Status |
Reason |
antiwork/gumroad |
Stretch target |
Important repo, but it is Inertia-first and is not the cleanest first proof point |
exercism/website |
Process-blocked |
Useful technically, but the repo documents that outside PRs are not accepted |
Whale Targets
These are the high-value proof points that should come after the first wave is clearly credible.
| Repo |
Stars |
Current Integration |
Notes |
mastodon/mastodon |
49,825 |
vite_rails + React |
Probably the most visible Rails + React migration story if a narrow slice can be found |
gitlabhq/gitlabhq |
24,295 |
vite_rails + React |
Worth documenting, but too broad for the first implementation wave |
Institutional Middle Tier
These are not whales, but they are still strong proof points once the first wave is done.
| Repo |
Stars |
Current Integration |
Why it matters |
Recommendation |
thecartercenter/nemo |
65 |
react-rails + shakapacker |
Institutionally credible and still close to the classic migration story |
Keep using ready PR #1110 as the second-wave institutional proof point; the first slice is maintainability-first, but it already shows an honest shared-bundle reduction and a clean coexistence path |
Codeminer42/cm42-central |
324 |
vite_rails + React |
Strong star count and real page-scoped mounts instead of a catch-all SPA shell |
Move this up with nemo; draft PR #1029 is now open for the validated projects.index slice |
broadinstitute/single_cell_portal_core |
70 |
vite_rails + custom window.SCP.renderComponent bridge |
Broad Institute credibility plus a repo-owned Rails-to-React mount contract makes it a meaningful non-demo proof point |
Promote alongside cm42-central; draft PR #2381 is open for the validated studies#usage_stats slice, and the remaining gap is Mongo-backed controller/request proof |
digitalepidemiologylab/crowdbreaks |
12 |
Webpacker + webpacker-react |
Research-institution ownership gives it some signal, but it is the weakest of this tier |
De-prioritize behind the other institutional targets |
Reference Repos Already On React on Rails
These are useful references. Some are also legitimate upgrade-PR candidates.
| Repo |
Current stack snapshot |
Upgrade fit |
Recommendation |
ifmeorg/ifme |
react_on_rails 12.0.1, webpacker 5.4.4, React 17 |
Strong |
Fork branch justin808/ifme:codex/upgrade-react-on-rails-16 is pushed and locally revalidated; upstream PR is blocked only by the project's Slack-first contribution process |
houdiniproject/houdini |
gem react_on_rails 12.6.0, JS package 12.2.0, webpacker 5.4.4, React 17 |
Strong |
Still worth an upgrade PR, but current local work is blocked before app code because Ruby 3.0.7 is brittle on this macOS arm64 host and the patched local install was not healthy enough to run Bundler |
avalonmediasystem/avalon |
react_on_rails 14.2.1, shakapacker 8.3.0, React 19 |
Good |
Queue after ifme and houdini, but first fix the missing pinned active-fedora git revision in the baseline app |
sharetribe/sharetribe |
react_on_rails 13.0.2, React 16 |
Weak |
Do not prioritize; the repo says it is no longer actively maintained |
hostolab/covidliste |
react_on_rails 12.2.0, webpacker 5.3.0, React 17, Node 12 |
Weak |
Do not prioritize; activity looks too stale for unsolicited upgrade work |
Known Cross-Cutting Blockers
- Existing Rails apps use materially different integration stacks, so this is not a single codemod problem.
- Some repos have heavy local setup or native dependency chains that block fast validation, so benchmark and request-proof notes need a pinned Docker or service-backed environment instead of a half-working host install.
- Large repos often require very narrow PR scope to be reviewable upstream.
- Performance or maintainability evidence needs a shared method or the results will not be credible.
Related Issues and PRs
- Docs PR: #3126
- Proof-artifacts issue: #3127
- Legacy Webpacker compatibility follow-up: #3136
- React 16/17
react-dom/client warning follow-up: #3137
- Docs follow-up for legacy migration guidance: #3138
- Helper-name collision follow-up: #3143
- Vite package-manager check follow-up: #3145
- Draft follow-up needed:
react_on_rails:doctor and Shakapacker upgrade guidance for repos that keep package.json and node_modules under client/
- Related but different scope: #1985 for RSC migration success stories
- Framework-level benchmarking issue: #2169
- Benchmark workflow issue: #2546
What Is Still Missing
- A public-friendly publication path for the local proof notes
- A public benchmark note for at least one more repo beyond ACP.
nemo now has a local asset-comparison note, but it still needs a browser-level pass before it becomes the next stronger public benchmark
- A decision on whether Gumroad stays in the migration queue or becomes an Inertia case study
- A later whale-target scouting pass once the first wave lands
- A decision on which institutional middle-tier repo should become the next serious target after the current wave once
nemo maintainer feedback lands
- A reusable benchmark environment for side-by-side demos and videos that includes the service dependencies some apps need for request-level proof
- A product/docs answer for supported upgrades where JS dependencies intentionally live under
client/ rather than the Rails root
React on Rails Migration Examples: Meta Tracking Issue
This issue tracks open-source Rails applications that are strong candidates for incremental migration to
react_on_rails, along with the public PRs, blockers, benchmark notes, and follow-up work.Public docs should only carry durable example references. This issue is the better home for in-progress third-party PRs, maintainer-contact notes, and other working material that may change quickly.
The goal is not to rewrite large apps wholesale. The goal is to prove a repeatable migration path from existing Rails-plus-React stacks such as
react-rails,webpacker,shakapacker,vite_rails, and custom Rails React bridges.Why This Matters
Teams evaluating
react_on_railswant proof from real applications, not only toy examples.The most credible evidence is:
Selection Policy
First-Wave Status
EFForg/action-center-platformreact-railsdb:test:prepareplus the focused request/helper specs passthewca/worldcubeassociation.orgreact-railsdocker compose up --build wca_environment, and the compose-backed disclaimer request spec now pass after the branch restores legacyreact_component(...)calls through a coexistence overridedemarche-numerique/demarche.numerique.gouv.frvite_rails+ custom React bridgecoldwired/reactmount contract with a maintained helper contractcoldwiredupdating React components during DOM morphs, so this migration slice is not a fit for their architectureGSA/search-govreact-rails+ Shakapackercheckout code,jest,rspec, andcucumber; local maintainability note captured for splitting low-coupling SERP chrome into smaller mounts; PR rationale already explains the maintainability-first value of shrinking the shell boundary without rewriting the results bodyhackclub/hcbreact-railsDockerfilebuilds successfully, compose-backeddb:test:prepareplus the focused bookkeeping controller spec now pass, containerizedyarn build,yarn build:css, and focused RuboCop revalidation all pass once the app uses an explicit React-on-Rails alias plus a legacy-helper coexistence override; local maintainability note is now captured for that coexistence path; asset-level comparison showsbundle.jsmoved from2,739,714to2,754,760bytes whileyarn buildimproved from10,035 msto9,101 msbroadinstitute/single_cell_portal_corevite_rails+ customwindow.SCP.renderComponentbridgevite build --force --clearall pass for thestudies#usage_statsslice; asset-level comparison showsapplication-*.jsmoved from2,083,391to2,099,314bytes while full rebuild time moved from22,947 msto23,486 msSecond-Wave Targets
These matter, but they are weaker first proof points than the current wave.
thecartercenter/nemoreact-rails + shakapackerdb:setup, compile,react_on_rails:doctor, request spec, system spec, RuboCop, and focused JS lint all pass; shared global components chunk dropped from305,252to293,091bytesCodeminer42/cm42-centralvite_rails+ Reactdb:setup, focused controller spec, focused JS specs, ESLint, andvite buildall succeed; the builtapplication-*.jsbundle moved from2,193,537to2,209,328bytesbroadinstitute/single_cell_portal_corevite_rails+ customwindow.SCP.renderComponentbridgevite build --force --clearpass; the builtapplication-*.jsbundle moved from2,083,391to2,099,314bytesindentlabs/notebookreact-rails + Webpacker (ReactRailsUJS auto-mount)config/shakapacker.yml; the branch is therefore blocked on whether to add real Shakapacker config or stage a broader migration firstsaeloun/miru-webvite_rails+ SPA roothome#indexinto one global Vite entrypoint that mounts a single#react-root, so this is an app-shell case study rather than an incremental island migrationcodeforpdx/dwellingly-appreact-railswith SPA routing3.0.1not building on this macOS arm64 hostrubyforgood/demand-progressWebpacker+ ad hoc React mount2.5.1not building on this macOS arm64 hostStretch and Blocked Targets
antiwork/gumroadexercism/websiteWhale Targets
These are the high-value proof points that should come after the first wave is clearly credible.
mastodon/mastodonvite_rails+ Reactgitlabhq/gitlabhqvite_rails+ ReactInstitutional Middle Tier
These are not whales, but they are still strong proof points once the first wave is done.
thecartercenter/nemoreact-rails+shakapackerCodeminer42/cm42-centralvite_rails+ Reactnemo; draft PR #1029 is now open for the validatedprojects.indexslicebroadinstitute/single_cell_portal_corevite_rails+ customwindow.SCP.renderComponentbridgecm42-central; draft PR #2381 is open for the validatedstudies#usage_statsslice, and the remaining gap is Mongo-backed controller/request proofdigitalepidemiologylab/crowdbreaksWebpacker+webpacker-reactReference Repos Already On React on Rails
These are useful references. Some are also legitimate upgrade-PR candidates.
ifmeorg/ifmereact_on_rails12.0.1,webpacker5.4.4, React17justin808/ifme:codex/upgrade-react-on-rails-16is pushed and locally revalidated; upstream PR is blocked only by the project's Slack-first contribution processhoudiniproject/houdinireact_on_rails12.6.0, JS package12.2.0,webpacker5.4.4, React173.0.7is brittle on this macOS arm64 host and the patched local install was not healthy enough to run Bundleravalonmediasystem/avalonreact_on_rails14.2.1,shakapacker8.3.0, React19ifmeandhoudini, but first fix the missing pinnedactive-fedoragit revision in the baseline appsharetribe/sharetribereact_on_rails13.0.2, React16hostolab/covidlistereact_on_rails12.2.0,webpacker5.3.0, React17, Node12Known Cross-Cutting Blockers
Related Issues and PRs
react-dom/clientwarning follow-up: #3137react_on_rails:doctorand Shakapacker upgrade guidance for repos that keeppackage.jsonandnode_modulesunderclient/What Is Still Missing
nemonow has a local asset-comparison note, but it still needs a browser-level pass before it becomes the next stronger public benchmarknemomaintainer feedback landsclient/rather than the Rails root