react_router_rails_spa 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8b4be06b2470f7ece193c99c21dca1af7ae3856591399c8e16b6170bcc54c9e
4
- data.tar.gz: b845de80454767f463266504cff5028f098d9de7bb4837ff3de0d310c08ef5a5
3
+ metadata.gz: 95855ba78c935cea1d1c666a75769bf23dca4b3593b6837f29ceb2d97c3259b2
4
+ data.tar.gz: b4f53bab792448b9a8a1f7df9d6b02a37680425ab51fcdde40cc90c3b3d7dc50
5
5
  SHA512:
6
- metadata.gz: 17b47b7846d437d18bc48ff589f183b5a7b1a8d7e9406cd34ff9a3e19eab5d388facdbcb1685bf3df56a52a81e3300eb50fe6fbcbd627e73233f7eb1028d7633
7
- data.tar.gz: e781941c2404d2e5f7e41be2249cb3ed17fc56d3975d1a6d23324b02d1cf2b7fc05f9f3ed0784de29319dbcc18c9f7c2e9fa5e46445b40bb555d2a99c1e2937f
6
+ metadata.gz: 653a9a898765b134d1154e33169be82cd31d8143c775da3216e2106b7dad3b705a26d4fb94f07e30e200e478fd7d1398ae0308c883919e8a704d7a989ef53c6c
7
+ data.tar.gz: 813669a3b2801ddfad534b8ca1135f3f7dae018b8329f5bb112ced8fdf67d5367203c23f6f229b4b277ae645c6434e1e75c58f80cfbbb06c560fb4dea40133c9
data/.rubocop.yml CHANGED
@@ -1,8 +1,221 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 3.0
3
+ NewCops: enable
3
4
 
5
+ # Below taken from rubocop-rails-omakase
6
+
7
+ # Align `when` with `end`.
8
+ Layout/CaseIndentation:
9
+ Enabled: true
10
+ EnforcedStyle: end
11
+
12
+ # Align comments with method definitions.
13
+ Layout/CommentIndentation:
14
+ Enabled: true
15
+
16
+ Layout/ElseAlignment:
17
+ Enabled: true
18
+
19
+ Layout/EmptyLineAfterMagicComment:
20
+ Enabled: true
21
+
22
+ Layout/EmptyLinesAroundBlockBody:
23
+ Enabled: true
24
+
25
+ # In a regular class definition, no empty lines around the body.
26
+ Layout/EmptyLinesAroundClassBody:
27
+ Enabled: true
28
+
29
+ # In a regular method definition, no empty lines around the body.
30
+ Layout/EmptyLinesAroundMethodBody:
31
+ Enabled: true
32
+
33
+ # In a regular module definition, no empty lines around the body.
34
+ Layout/EmptyLinesAroundModuleBody:
35
+ Enabled: true
36
+
37
+ # Align `end` with the matching keyword or starting expression except for
38
+ # assignments, where it should be aligned with the LHS.
39
+ Layout/EndAlignment:
40
+ Enabled: true
41
+ EnforcedStyleAlignWith: variable
42
+
43
+ # Method definitions after `private` or `protected` isolated calls need one
44
+ # extra level of indentation.
45
+ #
46
+ # We break this rule in context, though, e.g. for private-only concerns,
47
+ # so we leave it disabled.
48
+ Layout/IndentationConsistency:
49
+ Enabled: false
50
+ EnforcedStyle: indented_internal_methods
51
+
52
+ # Detect hard tabs, no hard tabs.
53
+ Layout/IndentationStyle:
54
+ Enabled: true
55
+
56
+ # Two spaces, no tabs (for indentation).
57
+ #
58
+ # Doesn't behave properly with private-only concerns, so it's disabled.
59
+ Layout/IndentationWidth:
60
+ Enabled: false
61
+
62
+ Layout/LeadingCommentSpace:
63
+ Enabled: true
64
+
65
+ Layout/SpaceAfterColon:
66
+ Enabled: true
67
+
68
+ Layout/SpaceAfterComma:
69
+ Enabled: true
70
+
71
+ Layout/SpaceAroundEqualsInParameterDefault:
72
+ Enabled: true
73
+
74
+ Layout/SpaceAroundKeyword:
75
+ Enabled: true
76
+
77
+ # Use `foo {}` not `foo{}`.
78
+ Layout/SpaceBeforeBlockBraces:
79
+ Enabled: true
80
+
81
+ Layout/SpaceBeforeComma:
82
+ Enabled: true
83
+
84
+ Layout/SpaceBeforeFirstArg:
85
+ Enabled: true
86
+
87
+ # Use `->(x, y) { x + y }` not `-> (x, y) { x + y }`
88
+ Layout/SpaceInLambdaLiteral:
89
+ Enabled: true
90
+
91
+ # Use `[ a, [ b, c ] ]` not `[a, [b, c]]`
92
+ # Use `[]` not `[ ]`
93
+ Layout/SpaceInsideArrayLiteralBrackets:
94
+ Enabled: true
95
+ EnforcedStyle: space
96
+ EnforcedStyleForEmptyBrackets: no_space
97
+
98
+ # Use `%w[ a b ]` not `%w[ a b ]`.
99
+ Layout/SpaceInsideArrayPercentLiteral:
100
+ Enabled: true
101
+
102
+ # Use `foo { bar }` not `foo {bar}`.
103
+ # Use `foo { }` not `foo {}`.
104
+ Layout/SpaceInsideBlockBraces:
105
+ Enabled: true
106
+ EnforcedStyleForEmptyBraces: space
107
+
108
+ # Use `{ a: 1 }` not `{a:1}`.
109
+ # Use `{}` not `{ }`.
110
+ Layout/SpaceInsideHashLiteralBraces:
111
+ Enabled: true
112
+ EnforcedStyle: space
113
+ EnforcedStyleForEmptyBraces: no_space
114
+
115
+ # Use `foo(bar)` not `foo( bar )`
116
+ Layout/SpaceInsideParens:
117
+ Enabled: true
118
+
119
+ # Requiring a space is not yet supported as of 0.59.2
120
+ # Use `%w[ foo ]` not `%w[foo]`
121
+ Layout/SpaceInsidePercentLiteralDelimiters:
122
+ Enabled: false
123
+ #EnforcedStyle: space
124
+
125
+ # Use `hash[:key]` not `hash[ :key ]`
126
+ Layout/SpaceInsideReferenceBrackets:
127
+ Enabled: true
128
+
129
+ # Blank lines should not have any spaces.
130
+ Layout/TrailingEmptyLines:
131
+ Enabled: true
132
+
133
+ # No trailing whitespace.
134
+ Layout/TrailingWhitespace:
135
+ Enabled: true
136
+
137
+ Lint/RedundantStringCoercion:
138
+ Enabled: true
139
+
140
+ # Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
141
+ Lint/RequireParentheses:
142
+ Enabled: true
143
+
144
+ Lint/UriEscapeUnescape:
145
+ Enabled: true
146
+
147
+ # We generally prefer &&/|| but like low-precedence and/or in context
148
+ Style/AndOr:
149
+ Enabled: false
150
+
151
+ # Prefer Foo.method over Foo::method
152
+ Style/ColonMethodCall:
153
+ Enabled: true
154
+
155
+ Style/DefWithParentheses:
156
+ Enabled: true
157
+
158
+ # Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
159
+ Style/HashSyntax:
160
+ Enabled: true
161
+ EnforcedShorthandSyntax: either
162
+
163
+ # Defining a method with parameters needs parentheses.
164
+ Style/MethodDefParentheses:
165
+ Enabled: true
166
+
167
+ Style/ParenthesesAroundCondition:
168
+ Enabled: true
169
+
170
+ Style/PercentLiteralDelimiters:
171
+ Enabled: true
172
+ PreferredDelimiters:
173
+ default: "()"
174
+ "%i": "[]"
175
+ "%I": "[]"
176
+ "%r": "{}"
177
+ "%w": "[]"
178
+ "%W": "[]"
179
+
180
+ # Use quotes for string literals when they are enough.
181
+ Style/RedundantPercentQ:
182
+ Enabled: false
183
+
184
+ Style/RedundantReturn:
185
+ Enabled: true
186
+ AllowMultipleReturnValues: true
187
+
188
+ Style/Semicolon:
189
+ Enabled: true
190
+ AllowAsExpressionSeparator: true
191
+
192
+ Style/StabbyLambdaParentheses:
193
+ Enabled: true
194
+
195
+ # Use `"foo"` not `'foo'` unless escaping is required
4
196
  Style/StringLiterals:
197
+ Enabled: true
5
198
  EnforcedStyle: double_quotes
6
199
 
7
- Style/StringLiteralsInInterpolation:
8
- EnforcedStyle: double_quotes
200
+ Style/TrailingCommaInArrayLiteral:
201
+ Enabled: true
202
+
203
+ Style/TrailingCommaInHashLiteral:
204
+ Enabled: true
205
+
206
+ # Other cops
207
+
208
+ Style/Documentation:
209
+ Enabled: false
210
+
211
+ Style/FrozenStringLiteralComment:
212
+ Enabled: false
213
+
214
+ Metrics/BlockLength:
215
+ Enabled: true
216
+ Exclude:
217
+ - "**/*.gemspec"
218
+ - "**/*.rake"
219
+
220
+ Gemspec/RequireMFA:
221
+ Enabled: true
data/README.md CHANGED
@@ -1,40 +1,34 @@
1
1
  ## React Router SPA Framework mode integration for Ruby on Rails
2
2
 
3
3
  The react_router_rails_spa gem integrates [React Router in SPA Framework mode](https://reactrouter.com/how-to/spa) with your Ruby on Rails application.
4
- The React app is built as static assets in your Rails `public` folder, and so it can be deployed on your current production server with minimal, if any, configuration changes.
5
4
 
6
- Benefit from state-of-the-art React SPA technlologies
7
- the integrated client-side router, loader data-fetching pattern,
8
- automatic code-splitting, and development server with HMR,
9
- without the costs and complexities of running a separate front-end server.
5
+ The React app is built as a static SPA.
6
+ Static assets will be built in your Rails `public` folder,
7
+ and will be deployed on your current production server with minimal, if any, configuration changes.
10
8
 
11
- If you are using Next.js, not for SEO
12
- but only for convenience,
13
- if you are not using SSR but fetching data on the client-side,
14
- then this gem will provide you with a similarly convenient but simpler, cheaper and more performant solution.
9
+ With a single gem and a single command,
10
+ this gem sets up all that you need in ["Omakase"](https://dhh.dk/2012/rails-is-omakase.html)-style – An integrated client-side router,
11
+ per-route code-splitting, the loader data-fetch pattern, Rails controllers and routes
12
+ All of this will be automatically set up for you.
15
13
 
16
- You may be considering the traditional approaches to React and Rails integration such as [Webpacker](https://github.com/rails/webpacker),
17
- [jsbundling-rails](https://github.com/rails/jsbundling-rails), [Vite Ruby](https://github.com/ElMassimo/vite_ruby)
18
- but concerned about installing and configuring additional packages, and avoiding SPA pitfalls.
19
- With a single gem and a single command, this gem sets up all that you need in "Omakase"-style – Rails route and controller setup, an integrated client-side router, automatic per-route code-splitting, and the loader data-fetch pattern to eliminate data-fetch waterfalls.
14
+ [Read the introduction](https://github.com/naofumi/react_router_rails_spa/blob/main/documents/introduction.md) for more background:
20
15
 
21
16
  ## Who is it for?
22
17
 
23
- Consider trying out this gem if you fit any of the following descriptions.
18
+ Consider trying out this gem if any of the below apply to you.
24
19
 
25
- - You want to integrate React into a Ruby on Rails application.
26
- - You want an out-of-the-box solution for a state-of-the-art multipage React application. You do not enjoy installing React, React Router, Tailwind, configuring code-splitting, and deciding the data-loading scheme that you will use throughout your application. You want "Omakase" on the front-end as well as your Rails back-end.
27
- - Your Ruby on Rails application already has ERB (Hotwire) pages.
28
- - You do not need SEO for the React generated pages (you don't need SSR/SSG).
29
- - You can always use ERB views for the pages that neww SEO.
30
- - You do not want to host multiple servers for your frontend and your backend.
31
- - You do not want to incur the additional costs, complexity, and authentication concerns that are inherent when dealing with multiple servers.
20
+ - You want an out-of-the-box solution.
21
+ - You do not enjoy installing React, React Router, Tailwind, configuring code-splitting, and deciding the data-loading scheme that you will use throughout your application. You want ["Omakase"](https://dhh.dk/2012/rails-is-omakase.html) on the front-end as well as your Rails back-end.
22
+ - You do not need SEO, at least not for the React pages.
23
+ - You can always use ERB views for the pages that need SEO.
24
+ - You are tired of managing multiple servers for your frontend and your backend.
25
+ - You do not want to incur the additional costs, complexity, and authentication concerns that are inherent when dealing with multiple servers, for no concrete benefit.
32
26
  - You want to simply deploy your React frontend as static assets on a single server, inside your Ruby on Rails `public` folder.
33
27
  - You have many pages, and you want to reduce the initial JavaScript payload size by using automatic code-splitting and lazy-loading, but without sacrificing performance due to request waterfalls.
34
28
 
35
29
  ## Installation
36
30
 
37
- The React Router application will be installed inside the `frontend` directory. We assume that you have an existing Ruby on Rails application.
31
+ We assume that you already have an existing Ruby on Rails application.
38
32
 
39
33
  Add this line to your application's Gemfile:
40
34
 
@@ -46,48 +40,52 @@ Then, run:
46
40
 
47
41
  ```shell
48
42
  bundle install
43
+ ```
44
+
45
+ followed by:
46
+
47
+ ```shell
49
48
  bin/rails generate react_router_rails_spa:install
50
49
  ```
51
50
 
52
51
  This will create a new directory called `frontend` inside the project root.
53
- It will also create a React bootstrap endpoint for all paths starting with `/react`.
54
- The endpoint will be handled by `ReactController#show`.
52
+ This is where your React application is.
55
53
 
56
- You will also have rake tasks for starting the dev server and building/previewing the React app.
57
-
58
- As part of the integration, we provide utilities for using the robust CSRF protection built into Ruby on Rails from your React application.
54
+ It will also create a React bootstrap endpoint in your Rails routes for all paths starting with `/react`.
55
+ The endpoint will be handled by `ReactController#show`.
59
56
 
60
57
  ## Running the React Router development server
61
58
 
62
- React Router is built with Vite and uses the Vite development server to provide a Hot Module Replacement (HMR) capability, that is very helpful for routing editing of pages.
63
- However, the development server is not used in production and will not widely represent the application's behavior in production.
64
- We therefore strongly recommend that you build the React Router assets into the `public` folder and preview it before deploying into production.
59
+ React Router is built with Vite and uses the Vite development server to provide a Hot Module Replacement (HMR) capability.
65
60
 
66
- Start the Vite development server with HMR with the following command (we assume that the Ruby on Rails server is already running (with either `bin/rails s` or `bin/dev`).
61
+ Start the Vite development server with the following command
62
+ (we assume that the Ruby on Rails server is already running (with either the `bin/rails s` or the `bin/dev` command).
67
63
 
68
64
  ```shell
69
65
  bin/rails react_router:dev
70
66
  ```
71
67
 
72
- Access the development server from the URL outputted from this command (Typically `http://localhost:5173/react`)
68
+ Note that the development server will not fully represent the application's behavior in production.
69
+ In particular, the development server is only partially integrated with Rails.
70
+ We therefore strongly recommend that you build the React Router assets into the `public` folder and preview it before deploying into production.
73
71
 
74
72
  To preview the production build, run the following command.
75
73
  ```shell
76
74
  bin/rails react_router:preview
77
75
  ```
78
76
 
79
- This will build the React Router application into the Rails `public` folder, and the React Router application will be available from the Rails development server (puma) at `http://localhost:3000/react`.
80
- The preview will be representative of the production app's behavior.
77
+ This will build the React Router application into the Rails `public` folder.
78
+ The React Router application will be available from the Rails development server
79
+ (puma) at `http://localhost:3000/react`.
80
+ This preview will be representative of the production app's behavior with Rails integration.
81
81
 
82
82
  ## Deployment
83
83
 
84
- This gem integrates with the Ruby on Rails Asset pipeline.
85
-
86
- The React Router application is automatically built whenever `bin/rails assets:precompile` is run
87
- and therefore no changes should be required since your Ruby on Rails application should already do this.
84
+ This gem integrates with the Ruby on Rails Asset pipeline, and the React application is automatically built whenever `bin/rails assets:precompile` is run.
85
+ No changes are required on your deployment script, since your Ruby on Rails application should run this command.
88
86
 
89
87
  If your deployment pipeline does not already install Node (for example, you have a "no-build" deployment for Rails),
90
- then install it in your CI/CD environment since building the React Router application will require it.
88
+ then install it in your CI/CD environment since building the React Router application will require it.
91
89
 
92
90
  ## Background
93
91
 
@@ -128,6 +126,7 @@ Importantly, API mode implies a stateless API server that does not support cooki
128
126
  It removes the middleware for cookie handling and also for CSRF protection.
129
127
 
130
128
  To fully benefit from hosting your React app inside Rails' `public` folder, we recommend that you avoid API-mode and instead use cookies for authentication.
129
+
131
130
  If you want to convert your API-mode application to use cookies, make sure to also restore CSRF features.
132
131
  Otherwise, your app will be vulnerable to CSRF attacks.
133
132
 
@@ -2,42 +2,63 @@
2
2
 
3
3
  ## TL;DR;
4
4
 
5
- This article describes the [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa), which allows you to integrate a React Router SPA framework application into you existing Ruby on Rails project.
5
+ Integrating React onto a Ruby on Rails application is unnecessarily challenging.
6
+ We have to install jsbundling-rails, install multiple packages, configure propshaft, create an ERB endpoint, etc.
6
7
 
7
- To jump to the installation steps, go to the "Steps to Integrate React Router into your Rails Application" section.
8
+ Why can't we just run `rails new`,
9
+ install a single gem, and instantly have a state-of-the-art React setup with client-side routing,
10
+ code-splitting, and effective data-loading patterns built-in?
11
+
12
+ This article introduces the [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa),
13
+ which integrates a React Router SPA framework application into your existing Ruby on Rails project.
14
+ With just a few commands, you will have a fully functioning scaffold on which to build your React app.
15
+
16
+ To jump to the installation steps, go to the ["Using the gem" section](#using-the-gem).
8
17
 
9
18
  ## Who is this for?
10
19
 
11
- The react_router_rails_spa gem was designed for the following situations.
20
+ If any of the following describes yourself, then this gem might be for you.
12
21
 
13
- ### You want to create a web application with a React frontend and a Rails backend.
22
+ ### You want to create an SPA with a React frontend and a Rails backend.
14
23
 
15
- * You want a simple setup that is ["omakase"](https://dhh.dk/2012/rails-is-omakase.html). You don't want to install packages and configure them on the React side. You don't want to manually add routes and controllers on the Rails side. Everything should be a single gem and a single command.
16
- * You want something that is easy to deploy, and cost-effective. You don't want to worry about server charges.
24
+ * You want a simple setup that is ["omakase"](https://dhh.dk/2012/rails-is-omakase.html). You don't want to install packages and configure them on the React side. On the Rails side, you don't want to manually add routes and controllers. Everything should be a single gem and a single command.
25
+ * You want something that is easy to deploy, and cost-effective. You don't want to pay for an extra server that you don't strictly need.
17
26
  * You don't need SEO for the React pages.
18
27
  * If SEO is necessary, you can just serve ERB pages or static HTML files.
19
28
 
20
- ### You are considering Next.js, but you do not need SSR nor RSCs. You are only considering Next.js because it is easy to set up.
29
+ ### You tried Next.js, but you did not really need SSR nor RSCs. You were only interested in Next.js because you thought it was easy.
21
30
 
22
- * You're only using Next.js because you thought it was ["omakase"](https://dhh.dk/2012/rails-is-omakase.html). You actually found Rails integration harder than you bargained for.
23
- * You are worried about deployment. Specifically, you are not happy with Vercel pricing or the extra cost of an additional AWS ECS instance to host Next.js.
31
+ * You're only using Next.js because you thought it was ["omakase"](https://dhh.dk/2012/rails-is-omakase.html) and simple to set up.
32
+ * After a while, you've found that integrating the Next.js server and the Rails server is harder than you bargained for. You've had headaches around authentication schemes, cross-domains, subdomains, CORS settings, samesite cookies, CSRF mitigation, and reverse proxies, etc. You've realized that running separate frontend and backend servers is hard.
33
+ * You are not happy with the extra money you have to pay to Next.js.
24
34
 
25
- The react_router_rails_spa gem satisfies the above requirements and more.
26
- It will give you an ["omakase"](https://dhh.dk/2012/rails-is-omakase.html) Modern React SPA with a single command.
35
+ The [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa) gives you the simplicity of an opinionated SPA framework,
36
+ without the complexity of a mulit-server setup.
27
37
 
28
- ### You want to use React because you believe you can create better UIs
38
+ ### You want to use cutting-edge React
29
39
 
30
- * You're using React because you think you can create a better UI/UX compared to Hotwire (I actually think that this is untrue and Hotwire can create great UI/UXs, but that's a different discussion)
31
- * You want to use cutting-edge React capabilities like code-splitting, loader-based data fetching and more. You don't want to create a slow, bloated, legacy React app.
40
+ * You want to use cutting-edge React capabilities like code-splitting, loader-based data fetching and more, but you're not sure how to [set this up from scratch](https://react.dev/learn/build-a-react-app-from-scratch).
41
+ * You don't want to create a slow, bloated, legacy React app.
32
42
 
33
43
  ## Who is this NOT for?
34
44
 
35
45
  ### You want to embedd some React components on top of your ERB-rendered pages
36
46
 
37
- This is [the original way that React was used](https://react.dev/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page).
47
+ This is [how React was originally used](https://react.dev/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page).
48
+ React was built for this, and it's generally much simpler to set up than a multi-page SPA.
49
+
50
+ The current gem does not help you with this.
38
51
  If you wish to take this approach,
39
52
  you can build your own system or use Gems like [react-rails](https://github.com/reactjs/react-rails) and [turbo-mount](https://github.com/skryukov/turbo-mount).
40
- Turbo Mount uses Stimulus to mount components, and is therefore more robust if you are using Hotwire in your ERB views.
53
+ Turbo Mount uses Stimulus to mount components, and is more robust if you are using Hotwire in your ERB views.
54
+
55
+ ## How does this compare to [...]?
56
+
57
+ Compared to gems like [React on Rails](https://github.com/shakacode/react_on_rails) or [Intertia Rails](https://inertia-rails.dev/),
58
+ the current gem is just an installer and does virtually nothing to modify or add features to React Router.
59
+ This is a great advantage and ensures that frontend developers will feel right at home.
60
+
61
+ It also means that you can easily understand how it works. You can customize accordingly.
41
62
 
42
63
  ## Background
43
64
 
@@ -50,9 +71,9 @@ Importantly, and often lost in the public discourse, they were **NOT** recommend
50
71
  Instead, they were advocating for creating SPAs with [**SPA** frameworks](https://react.dev/blog/2025/02/14/sunsetting-create-react-app#how-to-migrate-to-a-framework)
51
72
  that could be deployed on a CDN, a static hosting service, or the `public` folder of a Ruby on Rails application.
52
73
 
53
- In the following, I will call SPAs built with an SPA framework, **"Modern React SPAs"**
74
+ In the following, we will call SPAs built with an SPA framework, **"Modern React SPAs"**
54
75
  to highlight that this is the current officially recommended approach.
55
- To contrast, I will call the ones that the React team is actively discouraging, **"Legacy React SPAs"**.
76
+ To contrast, we will call the ones that the React team is actively discouraging, **"Legacy React SPAs"**.
56
77
 
57
78
  > **"I have no interest nor use for SSR!
58
79
  I don't need SEO.
@@ -79,7 +100,7 @@ to tell us
79
100
  that we should not simply replace the deprecated Create React App with a newer but nonetheless still architecturally Legacy SPA.
80
101
  Instead, they strongly urge us to embrace Modern React SPAs and avoid these issues.
81
102
 
82
- I should note that Vite is essentially a bundler and a development server, with a plugin system that allows us to easily install various packages.
103
+ We should note that Vite is essentially a bundler and a development server, with a plugin system that allows us to easily install various packages.
83
104
  It is agnostic to the Legacy vs. Modern SPA debate.
84
105
  You can build a Legacy SPA using Vite, and you can also create a Modern SPA.
85
106
  Vite does not care either way, and the installer command `npm create vite@latest` gives you both templates.
@@ -93,9 +114,9 @@ The above solution is a Legacy SPA and will suffer from the same legacy issues.
93
114
  Instead, the React team is recommending that you integrate a Modern SPA framework using ...
94
115
 
95
116
  Well, actually, they don't have a concrete recommendation yet for Rails.
96
- As far as I know, nothing currently exists to easily integrate a Modern SPA with Rails.
117
+ As far as we know, nothing currently exists to easily integrate a Modern SPA with Rails.
97
118
 
98
- I hope to address this with this [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa).
119
+ We hope to address this with this [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa).
99
120
 
100
121
  ## Why we need a different approach for Rails integration
101
122
 
@@ -126,51 +147,41 @@ This is how the react_router_rails_spa gem works.
126
147
  It is important to note that the [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa) does nothing more than a stock React Router installation with some custom configuration,
127
148
  paired with the generation of a single Rails controller.
128
149
 
129
- There is very little custom code, and you can easily update your NPM packages independently of this gem. The generated code is also heavily commented to help you understand the internals for yourself.
130
-
131
- ### React Router
150
+ **There is very little custom code, and this is actually a huge advantage**.
151
+ This is a very thin wrapper around the official React Router installer and resilient against future changes.
152
+ If you wish, you can easily update and customize your NPM packages independently of this gem. The generated code is also heavily commented
153
+ to help you understand the internals for yourself.
132
154
 
133
- We install and use React Router in framework mode, [configured to generate an SPA build](https://reactrouter.com/how-to/spa).
134
- This will build static files that can be served from any static hosting provider, including the `public` folder in Rails.
155
+ ### React Router SPA framework mode
135
156
 
136
- One of these static files is the bootstrap HTML template (the root `index.html` file).
137
- We change the name of this file so that it will not be directly served from the `public` folder.
138
- Instead,
139
- we use a dedicated Rails controller to add Rails-generated HTTP headers and cookies
140
- and serve the file as the response body.
141
-
142
- After building, these static files will be transferred to the Rails `public` folder from which they can be deployed like any other static asset.
157
+ We install and use React Router in SPA framework mode, [configured to generate an SPA build](https://reactrouter.com/how-to/spa).
158
+ It is a true SPA and will build static files that can be served from any static hosting provider.
159
+ These are transferred to the `public` folder in Rails for deployment.
143
160
 
144
- ### Rails routes.rb and the ReactController
161
+ The command for building the react application is integrated into the `rake assets:precompile` command.
162
+ Therefore, you do not need any additional configuration in your CI/CD scripts.
145
163
 
146
- We generate a `ReactController` that serves the bootstrap HTML template.
147
- The body of the response is the exact contents of the React Router-generated `index.html` file, but
148
- by passing it through the `ReactController`,
149
- we can customize the headers and add session-specific information as cookies.
150
-
151
- For example, `ReactController`
152
- includes the `ReactRouterRailsSpa::CsrfCookieEnabled` module
153
- which sends session-specific CSRF tokens via cookies to integrate Rails'
154
- CSRF protection with React.
164
+ ### Integration with Ruby on Rails through cookies
155
165
 
156
- `ReactController` will also add session cookies so that you can take advantage of session information from the bootstrap HTML file onwards.
166
+ Previously, you would integrate Ruby on Rails using a bootstrap HTML template generated by Rails using ERB templates.
167
+ This allowed you, for example, to embed CSRF-mitigation token tags.
168
+ As mentioned above, this is incompatible with Modern SPA frameworks.
157
169
 
158
- Finally, the `ReactController` allows you to set cache headers separately from assets served directly from the `assets` folder.
170
+ Otherwise,
171
+ you could build your SPA independently of Rails
172
+ and send extra requests from the browser to retrieve the CSRF token and other integration information.
173
+ This requires otherwise unnecessary network requests.
159
174
 
160
- Rails uses the [ActionDispatch::Static middleware](https://api.rubyonrails.org/classes/ActionDispatch/Static.html)
161
- to serve assets from the `public` folder,
162
- and this sets the HTTP caching headers aggressively to allow extensive caching for long periods of time.
163
- While this is great for JavaScript, CSS and image assets,
164
- this is usually undesirable for HTML files since we cannot attach cache-busting digests to them.
165
- Serving the bootstrap HTML template through `ReactController` allows us
166
- to easily change the cache headers to values that are suitable for HTML responses.
167
- Currently, the [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa) uses the same cache headers as other ERB files,
168
- but this can be customized in the controller.
175
+ Neither approach is ideal.
169
176
 
170
- Another way to look at this integration is like this;
177
+ Instead,
178
+ the [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa) sends Rails-generated cookies and/or headers alongside the SPA framework-generated bootstrap HTML file.
179
+ It give you the best of both worlds.
171
180
 
172
- * The traditional approach taken by webpack, esbuild and vite-rails, is to communicate between the React application and Rails via a Rails generated ERB bootstrap HTML. For example, Rails-generated CSRF tokens were provided as `meta` tags embedded in HTML.
173
- * The current approach is to use the React Router generated bootstrap HTML and to add Rails integration through HTTP headers (including cookies) only. That's why this gem sends Rails-generated CSRF tokens to the React application using cookies.
181
+ For example, the `ReactRouterRailsSpa::CsrfCookieEnabled` module
182
+ sends session-specific CSRF tokens via cookies to integrate Rails'
183
+ CSRF protection with React.
184
+ The SPA framework-generated HTML is untouched.
174
185
 
175
186
  ### Rake files for automation
176
187
 
@@ -191,31 +202,13 @@ All other paths are handled by Rails.
191
202
  The current gem adds minor configurations for this.
192
203
  If you want a different setup, you can change the configurations.
193
204
 
194
- Note that configurations for the Vite development server are tricky.
195
- We have provided settings
196
- to compensate for the fact that the development server runs on port 5173 while the Rails application runs on port 3000.
197
- However, this will not allow you to test integration between Rails and React.
198
-
199
- * The React app runs on port 5173 while the ERB files are on port 3000. Links between the two will not work on the development server, even if they are fine in production.
200
- * The React app running on the development server will not bootstrap from the Ruby on Rails endpoint on the `ReactController#show` action. Instead, the development server will directly serve the React Router generated bootstrap HTML. This means that the bootstrap file will not contain Rails integrations. This is only an issue on the development server and not in production.
201
-
202
- As a solution, you can use the development server with HMR for small fixes, but for larger changes,
203
- you will need to use a "preview" build.
204
- You should also always check with a "preview" build before deploying to production.
205
-
206
- We provide a rake task for building a "preview".
207
-
208
- ```shell
209
- bin/rails react_router:preview
210
- ```
211
-
212
205
  ## Demo and Source code
213
206
 
214
- * I have a [demo application based on the current proposal running on Kamal on a VPS server](https://rrrails.castle104.com/react-router/). It has simple, session-based authentication and basic CRUD. Mutations are secured by integration with Rails CSRF protection.
215
- * In the demo application, I have intentionally added a 0.5 to 1.5-second delay on all server requests. Even the most bloated and inefficient web technologies will look great on a high-performance device with a fast network. Unless your demo intentionally simulates non-ideal situations, it is meaningless.
207
+ * We have a [demo application running on Kamal on a VPS server](https://rrrails.castle104.com/react/). It has simple, session-based authentication and basic CRUD. Mutations are secured by integration with Rails CSRF protection.
208
+ * In the demo application, we have intentionally added a 0.5 to 1.5-second random delay on all server requests. Even the most bloated and inefficient web technologies will look great on a high-performance device with a fast network. Unless your demo intentionally simulates non-ideal situations, it is meaningless.
216
209
  * The source-code for this demo application is [available on GitHub](https://github.com/naofumi/react-router-vite-rails).
217
210
 
218
- The source code is heavily commented. I recommend that you read through it to understand the setup in more detail.
211
+ The source code is heavily commented. We recommend that you read through it to understand the setup in more detail.
219
212
 
220
213
  ## Using the gem
221
214
 
@@ -227,8 +220,16 @@ This gem works with a pre-existing installation of Rails. Create a new Rails app
227
220
  rails new [project name]
228
221
  ```
229
222
 
230
- Note that this gem works even with a no-build Rails setup (which is the Rails default),
231
- but you will need Node on your machine to install React Router.
223
+ Note that this gem works even with a no-build Rails setup (which is the Rails default).
224
+ However, you will need Node.js in your CI/CD for deployment.
225
+ If you are unsure how to do this,
226
+ we recommend that you generate a new Rails application with jsbundling-rails pre-installed.
227
+ This will create a ready-made Dockerfile that installs Node.js.
228
+ (This gem won't use esbuild. We're only doing this for Node.js.)
229
+
230
+ ```shell
231
+ rails new [project name] --javascript esbuild
232
+ ```
232
233
 
233
234
  ### Install the `react_router_rails_spa` gem
234
235
 
@@ -257,7 +258,7 @@ and generate the routes and all the necessary files and configurations.
257
258
 
258
259
  ### Run the development server
259
260
 
260
- Run the following command to start the development server. This comes with HMR (Hot Module Replacement).
261
+ Run the following command to start the frontend development server with HMR (Hot Module Replacement).
261
262
 
262
263
  ```shell
263
264
  bin/rails react_router:dev
@@ -265,7 +266,11 @@ bin/rails react_router:dev
265
266
 
266
267
  Point your browser to http://localhost:5173/react/ to see the welcome page.
267
268
 
268
- ### Build the React Router application
269
+ Note that the frontend development server will not truly represent
270
+ how the React application and Rails server interact in production.
271
+ It is important to test with the following preview command before deploying.
272
+
273
+ ### Preview and build the React Router application
269
274
 
270
275
  Run the following command to build the React Router application
271
276
  and store the static files into the Rails `public` directory.
@@ -274,17 +279,15 @@ and store the static files into the Rails `public` directory.
274
279
  bin/rails react_router:build
275
280
  ```
276
281
 
277
- Start your Rails application if it is not already running.
278
-
279
- Point your browser to http://localhost:3000/react/ to see the welcome page.
280
-
281
- This command is also aliased as `preview`.
282
+ This command is also aliased as:
282
283
 
283
284
  ```shell
284
285
  bin/rails react_router:preview
285
286
  ```
286
287
 
287
- ### Read the added code
288
+ Start your Rails application if it is not already running and point your browser to http://localhost:3000/react/ to see the welcome page.
289
+
290
+ ### Read the code
288
291
 
289
- I have added numerous comments to the code generated by this gem. Please read it to understand how the integration works.
292
+ We have added numerous comments to the code generated by this gem. Please read it to understand how the integration works.
290
293
 
@@ -18,7 +18,7 @@ namespace :react_router do
18
18
  #
19
19
  # bin/rails react_router:dev
20
20
  desc "Start React Router Development Server with Hot Module Reloading"
21
- task dev: [:npm_install] do
21
+ task dev: [ :npm_install ] do
22
22
  puts "Starting React Router v7 app development server..."
23
23
  Dir.chdir("#{Dir.pwd}/frontend") do
24
24
  system("npm", "run", "dev")
@@ -27,7 +27,7 @@ namespace :react_router do
27
27
 
28
28
  # bin/rails react_router:typecheck
29
29
  desc "Check Typescript for the React Router App"
30
- task typecheck: [:npm_install] do
30
+ task typecheck: [ :npm_install ] do
31
31
  puts "Check Typescript for React Router v7 app..."
32
32
  Dir.chdir("#{Dir.pwd}/frontend") do
33
33
  system("npm", "run", "typecheck")
@@ -42,7 +42,7 @@ namespace :react_router do
42
42
  #
43
43
  # bin/rails react_router:build
44
44
  desc "Build React Router App and move to the public folder"
45
- task build: [:npm_install] do
45
+ task build: [ :npm_install ] do
46
46
  puts "Building React Router v7 app..."
47
47
  `cd frontend && npm run build`
48
48
 
@@ -59,7 +59,7 @@ namespace :react_router do
59
59
  # This is identical to running bin/rails react_router: build
60
60
  # and is provided solely to align better with intent.
61
61
  desc "Preview your React Router App from the Rails development server (typically port 3000)"
62
- task preview: [:build]
62
+ task preview: [ :build ]
63
63
 
64
64
  # Run bin/rails react_router:clobber to remove the build files.
65
65
  # Running bin/rails assets:clobber will also run this task.
@@ -75,5 +75,5 @@ end
75
75
  # This means that any normal Rails deployment script which
76
76
  # contains rake assets:precompile will also build the
77
77
  # React Router app automatically.
78
- Rake::Task["assets:precompile"].enhance(["react_router:build"])
79
- Rake::Task["assets:clobber"].enhance(["react_router:clobber"])
78
+ Rake::Task["assets:precompile"].enhance([ "react_router:build" ])
79
+ Rake::Task["assets:clobber"].enhance([ "react_router:clobber" ])
@@ -61,7 +61,7 @@ const resources = [
61
61
  text: "Read the README for the react_router_rails_spa gem",
62
62
  },
63
63
  {
64
- href: "https://github.com/naofumi/react_router_rails_spa/documentation/introduction.md",
64
+ href: "https://github.com/naofumi/react_router_rails_spa/blob/main/documents/introduction.md",
65
65
  text: "Read the Introduction for the react_router_rails_spa gem",
66
66
  },
67
67
  {
@@ -69,7 +69,7 @@ const resources = [
69
69
  text: "Read the React Router Docs",
70
70
  },
71
71
  {
72
- href: "https://reactrouter.com/docs",
72
+ href: "https://rubyonrails.org/",
73
73
  text: "Read the Ruby on Rails Docs",
74
74
  },
75
75
  ];
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReactRouterRailsSpa
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: react_router_rails_spa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naofumi Kagami
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-20 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rails
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 8.0.2
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 8.0.2
11
+ date: 2025-04-22 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description:
28
14
  email:
29
15
  - naofumi@mac.com
@@ -67,6 +53,7 @@ metadata:
67
53
  homepage_uri: https://github.com/naofumi/react_router_rails_spa
68
54
  source_code_uri: https://github.com/naofumi/react_router_rails_spa
69
55
  changelog_uri: https://github.com/naofumi/react_router_rails_spa
56
+ rubygems_mfa_required: 'true'
70
57
  post_install_message:
71
58
  rdoc_options: []
72
59
  require_paths: