You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+48-31Lines changed: 48 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,43 +1,48 @@
1
1
# React-Router-Vite-Rails
2
2
3
-
This is an example web application proposing a way to integrate React Router Framework/SPA mode and Ruby on Rails.
3
+
This is an example web application that proposes a way to integrate React Router Framework/SPA mode and Ruby on Rails.
4
4
5
-
Consider using this if you wish to easily create a better React SPA integrated with Ruby on Rails.
5
+
Consider using the methods in this example
6
+
to explore a simple way to create a better React SPA integration for Ruby on Rails.
6
7
7
8
[Jump to how to build it](#how-it-is-built)
8
9
[See this example app deployed using Kamal](https://rrrails.castle104.com)
9
10
10
11
## The problem
11
12
12
13
Historically,
13
-
integrating React with Ruby on Rails was done by creating a dedicated Rails route and ERB file as the bootstrap file
14
+
integrating React with Ruby on Rails was done by creating a dedicated Rails route from which you served a static ERB file as the bootstrap file
14
15
(the first HTML file that the browser loads).
15
16
16
-
You would use `javascript_include_tag` (jsbundling or sprockets)
17
-
or `javascript_pack_tag` (webpacker) to load the JavaScript bundle that contains your React application.
18
-
If you wish to use Vite,
17
+
Inside this ERB file, you would typically use `javascript_include_tag` ([jsbundling](https://github.com/rails/jsbundling-rails) or [sprockets](https://github.com/rails/sprockets))
18
+
or `javascript_pack_tag` ([webpacker](https://github.com/rails/webpacker)) to load the JavaScript bundle that contained your React application.
19
+
If you wished to use Vite,
19
20
you might use [Vite Rails](https://github.com/ElMassimo/vite_ruby/tree/main/vite_rails)
20
-
and then use the `vite_javascript_tag`.
21
+
and then use the corresponding `vite_javascript_tag` in exactly the same way.
21
22
22
-
However, the traditional approach treats React as a library.
23
-
You are responsible for installing the client-side router, implementing code-splitting, avoiding fetch waterfalls, etc.
24
-
Although this approach may have been adequate in the past, the React team has recommended against it and suggested that [even for SPAs, developers should use a framework](https://react.dev/blog/2025/02/14/sunsetting-create-react-app).
23
+
However, this traditional approach treats React as a library.
24
+
You are responsible for installing the client-side router,
all of which have significant implications for performance.
27
+
Although this approach may have been adequate in the past,
28
+
the React team has recommended against it and suggested that [even if you are only interested in a SPA,
29
+
developers should use a framework](https://react.dev/blog/2025/02/14/sunsetting-create-react-app).
25
30
26
-
With this in mind, I propose a solution that allows you to easily integrate an SPA framework with Ruby on Rails.
31
+
With this in mind, I propose a simple solution that allows you to integrate an SPA framework with Ruby on Rails.
27
32
28
33
## The proposal
29
34
30
35
The current proposal uses React Router version 7 in SPA/Framework mode.
31
36
32
37
* Instead of creating a bootstrap file (the HTML file that is initially loaded by the browser) in ERB, we use the one that React Router builds in SPA mode (using SSG).
33
-
* We send the static bootstrap file through Rails controllers instead of serving it in the `public` directory. This allows us to set caching headers separately and manage cookies efficiently, simplifying authentication and CSRF protection.
38
+
* We send this static bootstrap file to the browser through Rails controllers instead of serving it in the `public` directory. This allows us to set caching headers separately and manage cookies efficiently, simplifying authentication and CSRF protection.
34
39
35
40
### Compared to the traditional method
36
41
37
42
* Compared to the traditional approach where React is treated as a library, an SPA framework will integrate a client-side routing library.
38
-
* It will also give you automatic code-splitting together with data-fetching parallelization and other benefits.
43
+
* It will also give you automatic code-splitting together with data-fetching parallelization and other benefits that are important for performance and UX.
39
44
40
-
Putting it simply, it should make it easier to create a better SPA.
45
+
Putting it simply, the current method should make it easier to create a better SPA.
41
46
42
47
### Compared to hosting static files on a separate server
43
48
@@ -50,31 +55,35 @@ Putting it simply, it should make it easier to create a better SPA.
50
55
* Hosting a separate Next.js SSR server will typically require you to re-implement authentication inside Next.js. The current approach can simply use Rails' authentication as is.
51
56
* With the current approach, cross-site requests and CORS will no longer be a concern.
52
57
53
-
Note that you can use Next.js as an SPA
54
-
and [use static export](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#static-export-optional).
55
-
This will also allow you
56
-
to host static files in the Ruby on Rails `public` folder although there are difficulties with dynamic routes.
58
+
Note that you can use Next.js [with static exports](https://nextjs.org/docs/app/building-your-application/upgrading/single-page-applications#static-export-optional)
59
+
to generate files that can be statically hosted as an SPA.
60
+
You could put these on a Ruby on Rails `public` folder to achieve simplicity that is similar to the current approach.
61
+
However, Next.js static exports have difficulties, particularly with dynamic routes,
62
+
which make it a less straightforward than React Router v7 in SPA mode.
63
+
Hence, my choice of React Router v7 instead.
57
64
58
65
## Building the Integration
59
66
60
-
I have heavily added comments to each file. Please look through these to see how the application is configured.
67
+
I have heavily added comments to each file in this repository.
68
+
Please read these to understand how the application is configured.
61
69
62
70
The step-by-step setup is as follows.
63
71
64
72
### Install Ruby on Rails
65
73
66
74
Just run `rails new` to set up the Ruby on Rails server.
67
-
A no-build setup will suffice since Rails is not responsible for building the React application.
68
-
If you want, you can use jsbundling for additional JavaScript.
69
-
Note that any build using jsbundling will be completely independent of the React app.
75
+
The default, no-build setup will suffice since Rails is not responsible for building the React application.
76
+
If you want, you can use [jsbundling](https://github.com/rails/jsbundling-rails) for additional JavaScript.
77
+
Note that the React Router app will be completely independent of jsbundling.
70
78
71
79
```shell
72
80
rails new [react-router-vite-rails]
73
81
```
74
82
75
83
\[react-router-vite-rails] is the name of the project.
76
84
77
-
Prepare a route and a controller action to serve the bootstrap HTML template. View the following files in the current directory for guidance.
85
+
Prepare a route and a controller action to serve the bootstrap HTML template.
86
+
View the comments in the following files for guidance.
This will set up the development server proxy, and move files to Rails' `public` directory on build.
102
-
Also, set up the Ruby rake tasks for running the development server and building assets.
111
+
You will additionally need to set up the Ruby rake tasks for running the development server and building assets.
103
112
The comments in the following files inside the current repository should guide you.
104
113
105
114
*`frontend-react-router/react-router.config.ts`
@@ -135,7 +144,8 @@ The example application simulates a slow server by adding a 2-second delay to al
135
144
This gives us a more realistic experience similar to what users might see in the real world.
136
145
Note that routes that don't need server connections will be instantaneous.
137
146
138
-
Any fast website will have a good UI/UX, even with ancient technology, and we need a slower one to demonstrate the benefits of using an SPA framework.
147
+
A fast server and a good internet connection will mask any deficiencies of a poorly built SPA.
148
+
To understand the benefits of using an SPA framework, I believe that you need to simulate real-world conditions with a slower network.
139
149
140
150
### Authentication
141
151
@@ -156,21 +166,28 @@ The server sends the CSRF token in a cookie, which can be added to the header of
156
166
157
167
Note that because we serve the bootstrap HTML template from a controller action,
158
168
the React application is guaranteed to have access to the CSRF token from the first load onwards.
159
-
This is convenient when the first page contains a form.
169
+
This is convenient when the first loaded page contains a form
170
+
and needs a valid CSRF token from the onset to allow immediate submission.
160
171
161
172
### Cache Control
162
173
163
174
The bootstrap HTML template is served from a Rails controller action.
164
175
As a result, the default Cache-Control header is set to the same value as other ERB files – `Cache-Control: max-age=0, private, must-revalidate`.
165
176
On the other hand, assets are served from the `public` folder and have `Cache-Control: public, max-age=172800`.
166
177
167
-
This ensures that assets use the browser cache effectively, whereas the HTML template is always fresh and users get the most recent version of the app.
178
+
This ensures that assets served from `public` use the browser cache effectively,
179
+
whereas the HTML template served from the Rails controller is always fresh and always provides the most recent version of the app.
180
+
181
+
Note that assets served from the `public` folder will have hash-digests to bust caching.
182
+
However, since the bootstrap HTML template is the first file to be loaded,
183
+
it always has to have the same URL, and this precludes the use of hash-digests on this one.
184
+
Therefore, we need separate caching configurations for each.
168
185
169
186
### Deployment
170
187
171
-
Yarn installation and the React Router build step are integrated into the `bin/rails assets:precompile` task.
188
+
NPM installation and the React Router build step are integrated into the `bin/rails assets:precompile` task.
172
189
Artifacts are stored inside the Ruby on Rails `public` folder.
173
-
Note that we don't use Propshaft for the React Router build, and therefore artifacts are not saved into `app/assets/builds`.
174
190
175
-
Your Dockerfile and CI setup can stay the same with the exception that you will need to install Node.js.
176
-
See the `Dockerfile` for an example.
191
+
Since we tap into the asset pipeline commands,
192
+
your Dockerfile and CI setup can stay the same with the exception that you may need to install Node.js.
0 commit comments