react_router_rails_spa 0.1.0 → 0.1.1
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 +4 -4
- data/README.md +118 -17
- data/documents/introduction.md +290 -0
- data/lib/react_router_rails_spa/csrf/csrf_cookie_enabled.rb +20 -0
- data/lib/react_router_rails_spa/generators/install_generator.rb +41 -7
- data/lib/react_router_rails_spa/generators/templates/csrf.ts +6 -2
- data/lib/react_router_rails_spa/generators/templates/csrf_cookie_enabled.rb +18 -0
- data/lib/react_router_rails_spa/generators/templates/proxy.ts +17 -10
- data/lib/react_router_rails_spa/generators/templates/react-router.config.ts +4 -5
- data/lib/react_router_rails_spa/generators/templates/react.rake +23 -31
- data/lib/react_router_rails_spa/generators/templates/vite.config.ts +18 -16
- data/lib/react_router_rails_spa/generators/templates/welcome/welcome.tsx +17 -7
- data/lib/react_router_rails_spa/version.rb +1 -1
- data/lib/react_router_rails_spa.rb +1 -0
- metadata +6 -4
- data/.idea/.name +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8b4be06b2470f7ece193c99c21dca1af7ae3856591399c8e16b6170bcc54c9e
|
4
|
+
data.tar.gz: b845de80454767f463266504cff5028f098d9de7bb4837ff3de0d310c08ef5a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17b47b7846d437d18bc48ff589f183b5a7b1a8d7e9406cd34ff9a3e19eab5d388facdbcb1685bf3df56a52a81e3300eb50fe6fbcbd627e73233f7eb1028d7633
|
7
|
+
data.tar.gz: e781941c2404d2e5f7e41be2249cb3ed17fc56d3975d1a6d23324b02d1cf2b7fc05f9f3ed0784de29319dbcc18c9f7c2e9fa5e46445b40bb555d2a99c1e2937f
|
data/README.md
CHANGED
@@ -1,38 +1,139 @@
|
|
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.
|
4
5
|
|
5
|
-
|
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.
|
10
|
+
|
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.
|
15
|
+
|
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.
|
20
|
+
|
21
|
+
## Who is it for?
|
22
|
+
|
23
|
+
Consider trying out this gem if you fit any of the following descriptions.
|
24
|
+
|
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.
|
32
|
+
- You want to simply deploy your React frontend as static assets on a single server, inside your Ruby on Rails `public` folder.
|
33
|
+
- 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.
|
6
34
|
|
7
35
|
## Installation
|
8
36
|
|
9
|
-
|
37
|
+
The React Router application will be installed inside the `frontend` directory. We assume that you have an existing Ruby on Rails application.
|
38
|
+
|
39
|
+
Add this line to your application's Gemfile:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
gem 'react_router_rails_spa'
|
43
|
+
```
|
44
|
+
|
45
|
+
Then, run:
|
46
|
+
|
47
|
+
```shell
|
48
|
+
bundle install
|
49
|
+
bin/rails generate react_router_rails_spa:install
|
50
|
+
```
|
51
|
+
|
52
|
+
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`.
|
10
55
|
|
11
|
-
|
56
|
+
You will also have rake tasks for starting the dev server and building/previewing the React app.
|
12
57
|
|
13
|
-
|
14
|
-
|
58
|
+
As part of the integration, we provide utilities for using the robust CSRF protection built into Ruby on Rails from your React application.
|
59
|
+
|
60
|
+
## Running the React Router development server
|
61
|
+
|
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.
|
65
|
+
|
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`).
|
67
|
+
|
68
|
+
```shell
|
69
|
+
bin/rails react_router:dev
|
15
70
|
```
|
16
71
|
|
17
|
-
|
72
|
+
Access the development server from the URL outputted from this command (Typically `http://localhost:5173/react`)
|
18
73
|
|
19
|
-
|
20
|
-
|
74
|
+
To preview the production build, run the following command.
|
75
|
+
```shell
|
76
|
+
bin/rails react_router:preview
|
21
77
|
```
|
22
78
|
|
23
|
-
|
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.
|
81
|
+
|
82
|
+
## Deployment
|
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.
|
88
|
+
|
89
|
+
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.
|
91
|
+
|
92
|
+
## Background
|
93
|
+
|
94
|
+
Ruby on Rails has officially supported bundling of complex JavaScript frontend libraries,
|
95
|
+
first with [Webpacker](https://github.com/rails/webpacker),
|
96
|
+
and currently with [jsbundling-rails](https://github.com/rails/jsbundling-rails).
|
97
|
+
We also have [Vite Ruby](https://github.com/ElMassimo/vite_ruby) with an integrated dev server, HMR and other goodies.
|
98
|
+
|
99
|
+
However, on February 14th, 2025,
|
100
|
+
the React team announced the [official deprecation of Create React App (CRA)](https://react.dev/blog/2025/02/14/sunsetting-create-react-app).
|
101
|
+
Instead, they suggested developers should use a framework that builds single-page apps
|
102
|
+
(SPA) deployable to a CDN or a static hosting service –
|
103
|
+
In other words, SPA frameworks such as Next.js or React Router.
|
104
|
+
Importantly,
|
105
|
+
they advised
|
106
|
+
against [building a React app from scratch](https://react.dev/learn/build-a-react-app-from-scratch)
|
107
|
+
unless your app has constraints not well-served by existing SPA frameworks.
|
108
|
+
|
109
|
+
This approach poses challenges to the traditional Rails integration solutions.
|
110
|
+
They all compile the React applications to a single JavaScript file
|
111
|
+
that is then loaded from an ERB file (the React bootstrap HTML) containing helper functions such as
|
112
|
+
`javascript_include_tag` (jsbundling-rails), `javascript_pack_tag` (webpacker), or `vite_javascript_tag` (vite-rails).
|
113
|
+
However, SPA frameworks do not just compile to a single JavaScript file.
|
114
|
+
Instead,
|
115
|
+
they additionally generate their own React bootstrap HTML file which includes many framework-specific optimizations
|
116
|
+
(React Router v7 generates this using SSG from `app/root.tsx`).
|
24
117
|
|
25
|
-
|
118
|
+
This gem uses the bootstrap HTML file
|
119
|
+
generated by the SPA framework instead of an ERB file.
|
120
|
+
This is served through an ActionController endpoint,
|
121
|
+
enabling you to use cookies to send session-specific information on the first HTML load
|
122
|
+
and is used in this gem to send CSRF tokens.
|
26
123
|
|
27
|
-
##
|
124
|
+
## Notes when using Rails in API mode
|
28
125
|
|
29
|
-
|
126
|
+
Ruby on Rails provides an API mode that removes many frontend features.
|
127
|
+
Importantly, API mode implies a stateless API server that does not support cookies.
|
128
|
+
It removes the middleware for cookie handling and also for CSRF protection.
|
30
129
|
|
31
|
-
To
|
130
|
+
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.
|
131
|
+
If you want to convert your API-mode application to use cookies, make sure to also restore CSRF features.
|
132
|
+
Otherwise, your app will be vulnerable to CSRF attacks.
|
32
133
|
|
33
134
|
## Contributing
|
34
135
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
136
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/naofumi/react_router_rails_spa. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/naofumi/react_router_rails_spa/blob/main/CODE_OF_CONDUCT.md).
|
36
137
|
|
37
138
|
## License
|
38
139
|
|
@@ -40,4 +141,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
40
141
|
|
41
142
|
## Code of Conduct
|
42
143
|
|
43
|
-
Everyone interacting in the
|
144
|
+
Everyone interacting in the ReactRouterRailSpa project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/naofumi/react_router_rails_spa/blob/main/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,290 @@
|
|
1
|
+
# Integrating Ruby on Rails with Modern SPAs
|
2
|
+
|
3
|
+
## TL;DR;
|
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.
|
6
|
+
|
7
|
+
To jump to the installation steps, go to the "Steps to Integrate React Router into your Rails Application" section.
|
8
|
+
|
9
|
+
## Who is this for?
|
10
|
+
|
11
|
+
The react_router_rails_spa gem was designed for the following situations.
|
12
|
+
|
13
|
+
### You want to create a web application with a React frontend and a Rails backend.
|
14
|
+
|
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.
|
17
|
+
* You don't need SEO for the React pages.
|
18
|
+
* If SEO is necessary, you can just serve ERB pages or static HTML files.
|
19
|
+
|
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.
|
21
|
+
|
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.
|
24
|
+
|
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.
|
27
|
+
|
28
|
+
### You want to use React because you believe you can create better UIs
|
29
|
+
|
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.
|
32
|
+
|
33
|
+
## Who is this NOT for?
|
34
|
+
|
35
|
+
### You want to embedd some React components on top of your ERB-rendered pages
|
36
|
+
|
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).
|
38
|
+
If you wish to take this approach,
|
39
|
+
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.
|
41
|
+
|
42
|
+
## Background
|
43
|
+
|
44
|
+
On February 14th, 2025,
|
45
|
+
the React team published a blog post
|
46
|
+
titled [Sunsetting Create React App](https://react.dev/blog/2025/02/14/sunsetting-create-react-app). They strongly recommended
|
47
|
+
that **developers should now use an SPA framework instead**.
|
48
|
+
|
49
|
+
Importantly, and often lost in the public discourse, they were **NOT** recommending an **SSR** framework.
|
50
|
+
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
|
+
that could be deployed on a CDN, a static hosting service, or the `public` folder of a Ruby on Rails application.
|
52
|
+
|
53
|
+
In the following, I will call SPAs built with an SPA framework, **"Modern React SPAs"**
|
54
|
+
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"**.
|
56
|
+
|
57
|
+
> **"I have no interest nor use for SSR!
|
58
|
+
I don't need SEO.
|
59
|
+
> An SPA is all that I need.
|
60
|
+
> Instead of Create React App, I'll just use Vite!"**
|
61
|
+
|
62
|
+
This was the most common response to the blog post.
|
63
|
+
However IMO, it misses the point that the authors were repeatedly trying to make.
|
64
|
+
|
65
|
+
The React team is strongly recommending that **even if you only need an SPA, you should be creating a Modern SPA**.
|
66
|
+
[They carefully go through some of the features](https://react.dev/learn/build-a-react-app-from-scratch) like code-splitting
|
67
|
+
and loader-based routing
|
68
|
+
that you will need to add if you are building a state-of-the-art React SPA from scratch.
|
69
|
+
These features are often challenging and require expertise to correctly implement,
|
70
|
+
but are essential for modern React applications.
|
71
|
+
|
72
|
+
Without these features, your React SPA is a Legacy SPA.
|
73
|
+
It will suffer from the same performance issues that plagued old SPAs a decade ago –
|
74
|
+
namely huge initial bundle size, data-fetch waterfalls, flickering, and very slow load times.
|
75
|
+
|
76
|
+
In the above article,
|
77
|
+
the React team went out of its way
|
78
|
+
to tell us
|
79
|
+
that we should not simply replace the deprecated Create React App with a newer but nonetheless still architecturally Legacy SPA.
|
80
|
+
Instead, they strongly urge us to embrace Modern React SPAs and avoid these issues.
|
81
|
+
|
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.
|
83
|
+
It is agnostic to the Legacy vs. Modern SPA debate.
|
84
|
+
You can build a Legacy SPA using Vite, and you can also create a Modern SPA.
|
85
|
+
Vite does not care either way, and the installer command `npm create vite@latest` gives you both templates.
|
86
|
+
|
87
|
+
> **"I want to integrate my React app with a Ruby on Rails backend. I'll just add a `javascript_include_tag` to my bootstrap ERB template. That will load React just fine!"**
|
88
|
+
|
89
|
+
This is also a common response from people who prefer a no-fuss solution for Rails.
|
90
|
+
However, note that this is exactly the approach that the React team strongly discourages.
|
91
|
+
The above solution is a Legacy SPA and will suffer from the same legacy issues.
|
92
|
+
|
93
|
+
Instead, the React team is recommending that you integrate a Modern SPA framework using ...
|
94
|
+
|
95
|
+
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.
|
97
|
+
|
98
|
+
I hope to address this with this [react_router_rails_spa gem](https://github.com/naofumi/react_router_rails_spa).
|
99
|
+
|
100
|
+
## Why we need a different approach for Rails integration
|
101
|
+
|
102
|
+
Historically,
|
103
|
+
the way to integrate React with Ruby on Rails was to create an ERB endpoint that served as the initial HTML
|
104
|
+
(the bootstrap HTML) for React.
|
105
|
+
This ERB template would have either a `javascript_include_tag` (jsbundling-rails) or a
|
106
|
+
`javascript_pack_tag` (webpacker) to load the React application build artifact.
|
107
|
+
Newer gems like [Vite Rails](https://vite-ruby.netlify.app/guide/rails.html#tag-helpers-🏷) have also adopted the same approach.
|
108
|
+
|
109
|
+
However, this is exactly what the React team is discouraging, and it seems unwise to continue down this path.
|
110
|
+
|
111
|
+
The problem is that Modern SPAs build their own bootstrap HTML templates with SSG
|
112
|
+
(the first HTML that the browser loads).
|
113
|
+
Modern SPA frameworks are not just JavaScript.
|
114
|
+
Instead, the bootstrap HTML and the JavaScript are tightly integrated.
|
115
|
+
|
116
|
+
Therefore, to take advantage of Modern SPA features,
|
117
|
+
Rails has
|
118
|
+
to give up
|
119
|
+
on generating its own bootstrap HTML from an ERB template with an embedded `javascript_include_tag`.
|
120
|
+
Instead, we have to take the bootstrap HTML generated by the SPA framework, and wrap Rails around this.
|
121
|
+
|
122
|
+
This is how the react_router_rails_spa gem works.
|
123
|
+
|
124
|
+
## Outline of how the gem works
|
125
|
+
|
126
|
+
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
|
+
paired with the generation of a single Rails controller.
|
128
|
+
|
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
|
132
|
+
|
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.
|
135
|
+
|
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.
|
143
|
+
|
144
|
+
### Rails routes.rb and the ReactController
|
145
|
+
|
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.
|
155
|
+
|
156
|
+
`ReactController` will also add session cookies so that you can take advantage of session information from the bootstrap HTML file onwards.
|
157
|
+
|
158
|
+
Finally, the `ReactController` allows you to set cache headers separately from assets served directly from the `assets` folder.
|
159
|
+
|
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.
|
169
|
+
|
170
|
+
Another way to look at this integration is like this;
|
171
|
+
|
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.
|
174
|
+
|
175
|
+
### Rake files for automation
|
176
|
+
|
177
|
+
We provide rake tasks for starting up the development server and building the React Router application.
|
178
|
+
|
179
|
+
Note that the build task is attached to the `assets:precompile` task.
|
180
|
+
This means
|
181
|
+
that you do not need
|
182
|
+
to add extra configuration to your CI/CD scripts
|
183
|
+
to build the React Router app since it should normally call this task already.
|
184
|
+
|
185
|
+
If your CI/CD already installs Node (which is required for building), then you probably won't have to touch your CI/CD scripts at all.
|
186
|
+
|
187
|
+
### Additional React Router and Vite Configuration
|
188
|
+
|
189
|
+
We currently serve the React application from the `/react/*` paths.
|
190
|
+
All other paths are handled by Rails.
|
191
|
+
The current gem adds minor configurations for this.
|
192
|
+
If you want a different setup, you can change the configurations.
|
193
|
+
|
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
|
+
## Demo and Source code
|
213
|
+
|
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.
|
216
|
+
* The source-code for this demo application is [available on GitHub](https://github.com/naofumi/react-router-vite-rails).
|
217
|
+
|
218
|
+
The source code is heavily commented. I recommend that you read through it to understand the setup in more detail.
|
219
|
+
|
220
|
+
## Using the gem
|
221
|
+
|
222
|
+
### Install Ruby on Rails
|
223
|
+
|
224
|
+
This gem works with a pre-existing installation of Rails. Create a new Rails application if you haven't already.
|
225
|
+
|
226
|
+
```shell
|
227
|
+
rails new [project name]
|
228
|
+
```
|
229
|
+
|
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.
|
232
|
+
|
233
|
+
### Install the `react_router_rails_spa` gem
|
234
|
+
|
235
|
+
Add the following line to your `Gemfile`.
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
gem "react_router_rails_spa"
|
239
|
+
```
|
240
|
+
|
241
|
+
Install the gem
|
242
|
+
|
243
|
+
```shell
|
244
|
+
bundle install
|
245
|
+
```
|
246
|
+
|
247
|
+
We recommend committing your changes at this point before the following generator adds and modifies your files.
|
248
|
+
|
249
|
+
Run the generator
|
250
|
+
|
251
|
+
```shell
|
252
|
+
bin/rails generate react_router_rails_spa:install
|
253
|
+
```
|
254
|
+
|
255
|
+
This will install the latest version of React Router
|
256
|
+
and generate the routes and all the necessary files and configurations.
|
257
|
+
|
258
|
+
### Run the development server
|
259
|
+
|
260
|
+
Run the following command to start the development server. This comes with HMR (Hot Module Replacement).
|
261
|
+
|
262
|
+
```shell
|
263
|
+
bin/rails react_router:dev
|
264
|
+
```
|
265
|
+
|
266
|
+
Point your browser to http://localhost:5173/react/ to see the welcome page.
|
267
|
+
|
268
|
+
### Build the React Router application
|
269
|
+
|
270
|
+
Run the following command to build the React Router application
|
271
|
+
and store the static files into the Rails `public` directory.
|
272
|
+
|
273
|
+
```shell
|
274
|
+
bin/rails react_router:build
|
275
|
+
```
|
276
|
+
|
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
|
+
|
283
|
+
```shell
|
284
|
+
bin/rails react_router:preview
|
285
|
+
```
|
286
|
+
|
287
|
+
### Read the added code
|
288
|
+
|
289
|
+
I have added numerous comments to the code generated by this gem. Please read it to understand how the integration works.
|
290
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This concern sets the CSRF token inside the "X-CSRF-Token" cookie,
|
2
|
+
# allowing you to easily use the robust CSRF protection that Ruby on Rails provides
|
3
|
+
# inside your React Router app.
|
4
|
+
#
|
5
|
+
# Refer to `frontend/app/utilities/csrf.ts` to see
|
6
|
+
# how the client side is implemented.
|
7
|
+
module ReactRouterRailsSpa
|
8
|
+
module CsrfCookieEnabled
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
included do
|
11
|
+
before_action :set_csrf_cookie
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def set_csrf_cookie
|
17
|
+
cookies["X-CSRF-Token"] = form_authenticity_token
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -6,19 +6,54 @@ module ReactRouterRailsSpa
|
|
6
6
|
source_root File.expand_path("templates", __dir__)
|
7
7
|
|
8
8
|
def create_react_router_app
|
9
|
-
say "
|
9
|
+
say "Downloading React Router v7 ..."
|
10
10
|
inside Rails.root do
|
11
11
|
run "npx create-react-router@latest frontend --yes --no-git-init"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
def setup_rails_routes
|
16
|
-
|
16
|
+
say "Setting up Rails routes ..."
|
17
|
+
route <<~RUBY
|
18
|
+
# When we use React Router inside a subdirectory, it works better if we
|
19
|
+
# use a trailing slash for the root path.
|
20
|
+
# This redirects from "/react-router" to "/react-router/".
|
21
|
+
get "react", to: redirect("/react/"), constraints: ->(req) {
|
22
|
+
req.original_url.last != "/"
|
23
|
+
}
|
24
|
+
|
25
|
+
# All requests to `/react/*` are handled by ReactController#show.
|
26
|
+
match "react", to: "react#show", via: :all
|
27
|
+
get "react/*path", to: "react#show"
|
28
|
+
RUBY
|
17
29
|
end
|
18
30
|
|
19
31
|
def create_react_controller
|
20
|
-
|
32
|
+
say "Creating the React bootstrap endpoint controller ..."
|
33
|
+
create_file "app/controllers/react_controller.rb", <<~RUBY
|
34
|
+
# This controller provides the catch-all action for React Router.
|
35
|
+
# This will render the bootstrap HTML file
|
36
|
+
# for all React Router requests.
|
37
|
+
#
|
38
|
+
# Unlike typical webpack or esbuild setups, we do not generate the bootstrap HTML file from ERB templates which include
|
39
|
+
# `javascript_include_tag` (propshaft, sprockets) or `javascript_pack_tag` (webpack).
|
40
|
+
# Instead, we take the index.html file that was generated by the React Router build
|
41
|
+
# and rename it to "react-router-rails-spa-index.html".
|
42
|
+
# We then serve this from the controller action in response to the bootstrap HTML file request.
|
43
|
+
#
|
44
|
+
# Benefits:
|
45
|
+
#
|
46
|
+
# * We can use the index.html file that React Router generates using SSG,
|
47
|
+
# from the `frontend/app/root.tsx` file.
|
48
|
+
# This file contains optimizations that would be challenging to recreate inside Rails using ERB templates.
|
49
|
+
# * By going through the Rails controller, we can adjust the cache and cookie headers to
|
50
|
+
# improve performance, reliability, and integration with Rails.
|
51
|
+
#
|
52
|
+
# The included ReactRouterRailsSpa::CsrfCookieEnabled module will
|
53
|
+
# send the CSRF token inside the "X-CSRF-Token" cookie for use inside your React app.
|
21
54
|
class ReactController < ApplicationController
|
55
|
+
include ReactRouterRailsSpa::CsrfCookieEnabled
|
56
|
+
|
22
57
|
def show
|
23
58
|
render file: Rails.public_path.join("react/react-router-rails-spa-index.html"), layout: false
|
24
59
|
end
|
@@ -26,20 +61,19 @@ module ReactRouterRailsSpa
|
|
26
61
|
RUBY
|
27
62
|
end
|
28
63
|
|
29
|
-
def create_public_folders
|
30
|
-
empty_directory Rails.root.join("public/react")
|
31
|
-
end
|
32
|
-
|
33
64
|
def copy_rake_task
|
65
|
+
say "Copying Rake tasks ..."
|
34
66
|
template "react.rake", "lib/tasks/react.rake"
|
35
67
|
end
|
36
68
|
|
37
69
|
def copy_react_router_configs
|
70
|
+
say "Copying React Router configurations ..."
|
38
71
|
template "react-router.config.ts", "frontend/react-router.config.ts"
|
39
72
|
template "vite.config.ts", "frontend/vite.config.ts"
|
40
73
|
end
|
41
74
|
|
42
75
|
def copy_react_app_files
|
76
|
+
say "Copying React Router application pages and utilities ..."
|
43
77
|
template "home.tsx", "frontend/app/routes/home.tsx"
|
44
78
|
directory "welcome", "frontend/app/welcome"
|
45
79
|
template "csrf.ts", "frontend/app/utilities/csrf.ts"
|
@@ -14,13 +14,13 @@
|
|
14
14
|
*
|
15
15
|
* How it works.
|
16
16
|
*
|
17
|
-
* 1. The Ruby on Rails server sends the session-specific
|
17
|
+
* 1. The Ruby on Rails server creates and sends the session-specific
|
18
18
|
* CSRF token in a Cookie named "X-CSRF-Token"
|
19
19
|
* app/controllers/concerns/csrf_cookie_enabled.rb
|
20
20
|
* 2. Retrieve this token with the `getCSRFToken()` function
|
21
21
|
* and use its value to send in your fetch request headers.
|
22
22
|
* 3. On receiving the request, Rails will validate that the
|
23
|
-
* 'X-CSRF-Token' header value
|
23
|
+
* 'X-CSRF-Token' header value is identical to the session-specific token.
|
24
24
|
* 4. Note that you only need to do this for non-GET requests assuming that
|
25
25
|
* you are following best practices and not mutating data in GET requests.
|
26
26
|
*
|
@@ -36,6 +36,10 @@
|
|
36
36
|
* }
|
37
37
|
* )
|
38
38
|
*
|
39
|
+
* Note that Axios has a feature that automatically does this for you.
|
40
|
+
* Read the XSRF configuration comments on the linked page.
|
41
|
+
* https://axios-http.com/docs/req_config
|
42
|
+
*
|
39
43
|
* */
|
40
44
|
|
41
45
|
export function getCSRFToken() {
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# This concern sets the CSRF token inside the "X-CSRF-Token" cookie,
|
2
|
+
# allowing you to easily use the robust CSRF protection that Ruby on Rails provides
|
3
|
+
# inside your React Router app.
|
4
|
+
#
|
5
|
+
# Refer to `frontend/app/utilities/csrf.ts` to see
|
6
|
+
# how the client side is implemented.
|
7
|
+
module CsrfCookieEnabled
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
included do
|
10
|
+
before_action :set_csrf_cookie
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def set_csrf_cookie
|
16
|
+
cookies["X-CSRF-Token"] = form_authenticity_token
|
17
|
+
end
|
18
|
+
end
|
@@ -1,17 +1,24 @@
|
|
1
1
|
/*
|
2
|
-
*
|
3
|
-
*
|
2
|
+
* The `baseApiPath()` is useful when you are using the React Router development server
|
3
|
+
* and the JSON API endpoints on your Rails server do not have specific prefixes.
|
4
|
+
* It is not used in production or preview builds.
|
4
5
|
*
|
5
|
-
*
|
6
|
+
*
|
7
|
+
* In the example application, the Ruby on Rails API endpoints are like
|
8
|
+
* `GET /posts` or `GET /users` and do not have any specific prefixes like `/api*`.
|
9
|
+
*
|
10
|
+
* However, during development, the port for the React Router development server (typically 5173) is different from
|
6
11
|
* the Rails development server (typically 3000).
|
7
|
-
* This means that the
|
12
|
+
* This means that the React Router server needs to distinguish
|
8
13
|
* between the requests it should send to the Rails server on port 3000 (JSON API requests) and the
|
9
|
-
* ones that it should handle itself on port 5173 (e.g., the
|
14
|
+
* ones that it should handle itself on port 5173 (e.g., the React Router assets).
|
10
15
|
*
|
11
|
-
* Therefore, when running on the
|
12
|
-
* requests intended for the Rails API server with "/api" to tell
|
16
|
+
* Therefore, when running on the React Router development server, we prefix
|
17
|
+
* requests intended for the Rails API server with "/api" to tell the development server to send them
|
13
18
|
* to port 3000.
|
14
|
-
*
|
19
|
+
* The all other requests will be sent to port 5173.
|
20
|
+
*
|
21
|
+
* See the `proxy:` section in `frontend/vite.config.ts` for
|
15
22
|
* the Vite side of this configuration.
|
16
23
|
*
|
17
24
|
* Using this function, you would write a request to the Rails API as follows.
|
@@ -20,8 +27,8 @@
|
|
20
27
|
* headers: { "Accept": "application/json" }
|
21
28
|
* }).then(...)....
|
22
29
|
*
|
23
|
-
* Note that if your API server's endpoints are like `/api/posts`,
|
24
|
-
* then you
|
30
|
+
* Note that if your JSON API server's endpoints are like `/api/posts`,
|
31
|
+
* then you don't need the `baseApiPath()` function.
|
25
32
|
*
|
26
33
|
* fetch(`/api/posts`, {
|
27
34
|
* headers: { "Accept": "application/json" }
|
@@ -2,11 +2,10 @@ import type { Config } from "@react-router/dev/config";
|
|
2
2
|
|
3
3
|
export default {
|
4
4
|
// Config options...
|
5
|
-
// For our React Router app, we will turn off Server-side render
|
6
|
-
// use SPA mode!!
|
5
|
+
// For our React Router app, we will turn off Server-side render and use SPA mode.
|
7
6
|
ssr: false,
|
8
|
-
//
|
9
|
-
// The basename
|
7
|
+
// We serve the React Router app from the "/react/" path.
|
8
|
+
// The basename option tells React Router to manage this when generating
|
10
9
|
// Link tags, for example.
|
11
|
-
basename: "/react
|
10
|
+
basename: "/react/"
|
12
11
|
} satisfies Config;
|
@@ -1,17 +1,4 @@
|
|
1
|
-
|
2
|
-
desc "Build the React application"
|
3
|
-
task :build do
|
4
|
-
puts "Building React Router v7 app..."
|
5
|
-
system("cd frontend && npm install && npm run build")
|
6
|
-
|
7
|
-
puts "Moving build files to public/react-router..."
|
8
|
-
system("rm -rf public/react-router/*")
|
9
|
-
system("mv frontend/dist/* public/react-router/")
|
10
|
-
system("mv public/react-router/index.html public/react-router/react-router-index.html")
|
11
|
-
|
12
|
-
puts "✅ React app successfully built and deployed!"
|
13
|
-
end
|
14
|
-
end
|
1
|
+
# frozen_string_literal: true
|
15
2
|
|
16
3
|
namespace :react_router do
|
17
4
|
# For convenience, npm packages do not have to be explicitly installed.
|
@@ -30,9 +17,9 @@ namespace :react_router do
|
|
30
17
|
# this task in the Procfile.
|
31
18
|
#
|
32
19
|
# bin/rails react_router:dev
|
33
|
-
desc "Start React Router
|
34
|
-
task dev: [
|
35
|
-
puts "Starting React Router v7 app
|
20
|
+
desc "Start React Router Development Server with Hot Module Reloading"
|
21
|
+
task dev: [:npm_install] do
|
22
|
+
puts "Starting React Router v7 app development server..."
|
36
23
|
Dir.chdir("#{Dir.pwd}/frontend") do
|
37
24
|
system("npm", "run", "dev")
|
38
25
|
end
|
@@ -40,7 +27,7 @@ namespace :react_router do
|
|
40
27
|
|
41
28
|
# bin/rails react_router:typecheck
|
42
29
|
desc "Check Typescript for the React Router App"
|
43
|
-
task typecheck: [
|
30
|
+
task typecheck: [:npm_install] do
|
44
31
|
puts "Check Typescript for React Router v7 app..."
|
45
32
|
Dir.chdir("#{Dir.pwd}/frontend") do
|
46
33
|
system("npm", "run", "typecheck")
|
@@ -54,21 +41,26 @@ namespace :react_router do
|
|
54
41
|
# Running bin/rails assets:precompile will also run this task.
|
55
42
|
#
|
56
43
|
# bin/rails react_router:build
|
57
|
-
desc "Build React Router App"
|
58
|
-
task build: [
|
59
|
-
|
60
|
-
|
61
|
-
system("npm", "run", "build")
|
44
|
+
desc "Build React Router App and move to the public folder"
|
45
|
+
task build: [:npm_install] do
|
46
|
+
puts "Building React Router v7 app..."
|
47
|
+
`cd frontend && npm run build`
|
62
48
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
49
|
+
puts "Moving build files to public/react..."
|
50
|
+
`rm -rf public/react`
|
51
|
+
`mv frontend/build/client public/react`
|
52
|
+
`mv public/react/index.html public/react/react-router-rails-spa-index.html`
|
67
53
|
|
68
|
-
|
69
|
-
end
|
54
|
+
puts "✅ React app successfully built and deployed!"
|
70
55
|
end
|
71
56
|
|
57
|
+
# Run bin/rails react_router:preview to create a preview build.
|
58
|
+
#
|
59
|
+
# This is identical to running bin/rails react_router: build
|
60
|
+
# and is provided solely to align better with intent.
|
61
|
+
desc "Preview your React Router App from the Rails development server (typically port 3000)"
|
62
|
+
task preview: [:build]
|
63
|
+
|
72
64
|
# Run bin/rails react_router:clobber to remove the build files.
|
73
65
|
# Running bin/rails assets:clobber will also run this task.
|
74
66
|
task :clobber do
|
@@ -83,5 +75,5 @@ end
|
|
83
75
|
# This means that any normal Rails deployment script which
|
84
76
|
# contains rake assets:precompile will also build the
|
85
77
|
# React Router app automatically.
|
86
|
-
Rake::Task["assets:precompile"].enhance([
|
87
|
-
Rake::Task["assets:clobber"].enhance([
|
78
|
+
Rake::Task["assets:precompile"].enhance(["react_router:build"])
|
79
|
+
Rake::Task["assets:clobber"].enhance(["react_router:clobber"])
|
@@ -3,32 +3,32 @@ import tailwindcss from "@tailwindcss/vite";
|
|
3
3
|
import { defineConfig } from "vite";
|
4
4
|
import tsconfigPaths from "vite-tsconfig-paths";
|
5
5
|
|
6
|
+
const railsEnv = process.env.RAILS_ENV || 'development'
|
7
|
+
|
6
8
|
export default defineConfig({
|
7
9
|
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
|
8
10
|
build: {
|
9
11
|
assetsDir: "assets",
|
10
|
-
// Set to true if you want sourcemaps
|
11
|
-
sourcemap: true,
|
12
|
+
// Set to true if you want sourcemaps in your build.
|
13
|
+
sourcemap: (["test", "development"].indexOf(railsEnv) !== -1) ? true : false,
|
12
14
|
},
|
13
|
-
//
|
14
|
-
// the app is served from the "/react
|
15
|
-
|
16
|
-
base: "/react-router/",
|
15
|
+
// As also defined in react-router.config.ts, this tells Vite that
|
16
|
+
// the app is served from the "/react/" sub-path.
|
17
|
+
base: "/react/",
|
17
18
|
|
18
19
|
// These are settings for the dev server.
|
19
20
|
server: {
|
20
|
-
// In production, the API server (Rails) and the assets
|
21
|
-
// will be served from the same port number (typically 80).
|
21
|
+
// In production and preview mode, the API server (Rails) and the assets
|
22
|
+
// will be served from the same port number (typically 80 for production and 3000 for preview).
|
22
23
|
//
|
23
|
-
// However,
|
24
|
-
//
|
25
|
-
//
|
26
|
-
// The use of different ports can complicate CORS setting and cookie handling.
|
24
|
+
// However, when using the vite development server, assets will be served from port 5173,
|
25
|
+
// and the API server will be on port 3000 (Rails default).
|
26
|
+
// The use of different ports complicates CORS settings and cookie handling.
|
27
27
|
//
|
28
|
-
// To
|
29
|
-
// on port (5173) and forward
|
30
|
-
//
|
31
|
-
//
|
28
|
+
// To work around this, Vite provides a proxy mode that can receive API requests
|
29
|
+
// on port (5173) and forward them to the Rails server (port 3000).
|
30
|
+
// From the browser's viewpoint, all communications including API requests to the Rails server
|
31
|
+
// will now happen on port 5173, so CORS will no longer be necessary.
|
32
32
|
proxy: {
|
33
33
|
// Any requests starting with "/api" will be forwarded according to
|
34
34
|
// the following rules.
|
@@ -44,6 +44,8 @@ export default defineConfig({
|
|
44
44
|
// Note that if the Rails server has a dedicated namespace for APIs,
|
45
45
|
// then you can just use that instead of "/api", and the following
|
46
46
|
// rewrite rule will not be necessary.
|
47
|
+
//
|
48
|
+
// Also see `frontend/app/utilities/proxy.ts` for how we handle this inside React.
|
47
49
|
rewrite: (path) => path.replace(/^\/api/, ""),
|
48
50
|
},
|
49
51
|
},
|
@@ -5,7 +5,7 @@ import railsLogo from "./rails-logo.svg";
|
|
5
5
|
export function Welcome() {
|
6
6
|
return (
|
7
7
|
<main className="flex items-center justify-center pt-16 pb-4">
|
8
|
-
<div className="flex-1 flex flex-col items-center
|
8
|
+
<div className="flex-1 flex flex-col items-center min-h-0">
|
9
9
|
<header className="flex flex-row items-baseline gap-9 max-w-[100vw]">
|
10
10
|
<div className="w-[300px]">
|
11
11
|
<img
|
@@ -28,16 +28,18 @@ export function Welcome() {
|
|
28
28
|
/>
|
29
29
|
</div>
|
30
30
|
</header>
|
31
|
-
<div className="
|
31
|
+
<div className="mt-12 text-2xl text-gray-600">with the</div>
|
32
|
+
<h1 className="mt-6 text-3xl font-bold">react_router_rails_spa gem</h1>
|
33
|
+
<div className="mt-12 max-w-[300px] w-full space-y-6 px-4">
|
32
34
|
<nav className="space-y-4">
|
33
|
-
<p className="leading-6 text-gray-700 dark:text-gray-200 text-center">
|
35
|
+
<p className="text-2xl leading-6 text-gray-700 dark:text-gray-200 text-center">
|
34
36
|
What's next?
|
35
37
|
</p>
|
36
38
|
<div className="text-center">
|
37
39
|
{resources.map(({ href, text }) => (
|
38
40
|
<a
|
39
41
|
key={href}
|
40
|
-
className="block mb-
|
42
|
+
className="block mb-4 leading-tight text-blue-700 hover:underline dark:text-blue-500"
|
41
43
|
href={href}
|
42
44
|
target="_blank"
|
43
45
|
rel="noreferrer"
|
@@ -54,12 +56,20 @@ export function Welcome() {
|
|
54
56
|
}
|
55
57
|
|
56
58
|
const resources = [
|
59
|
+
{
|
60
|
+
href: "https://github.com/naofumi/react_router_rails_spa",
|
61
|
+
text: "Read the README for the react_router_rails_spa gem",
|
62
|
+
},
|
63
|
+
{
|
64
|
+
href: "https://github.com/naofumi/react_router_rails_spa/documentation/introduction.md",
|
65
|
+
text: "Read the Introduction for the react_router_rails_spa gem",
|
66
|
+
},
|
57
67
|
{
|
58
68
|
href: "https://reactrouter.com/docs",
|
59
|
-
text: "React Router Docs",
|
69
|
+
text: "Read the React Router Docs",
|
60
70
|
},
|
61
71
|
{
|
62
|
-
href: "https://
|
63
|
-
text: "
|
72
|
+
href: "https://reactrouter.com/docs",
|
73
|
+
text: "Read the Ruby on Rails Docs",
|
64
74
|
},
|
65
75
|
];
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: react_router_rails_spa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
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-
|
11
|
+
date: 2025-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -32,7 +32,6 @@ extensions: []
|
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
34
|
- ".idea/.gitignore"
|
35
|
-
- ".idea/.name"
|
36
35
|
- ".idea/inspectionProfiles/Project_Default.xml"
|
37
36
|
- ".idea/misc.xml"
|
38
37
|
- ".idea/modules.xml"
|
@@ -44,9 +43,12 @@ files:
|
|
44
43
|
- LICENSE.txt
|
45
44
|
- README.md
|
46
45
|
- Rakefile
|
46
|
+
- documents/introduction.md
|
47
47
|
- lib/react_router_rails_spa.rb
|
48
|
+
- lib/react_router_rails_spa/csrf/csrf_cookie_enabled.rb
|
48
49
|
- lib/react_router_rails_spa/generators/install_generator.rb
|
49
50
|
- lib/react_router_rails_spa/generators/templates/csrf.ts
|
51
|
+
- lib/react_router_rails_spa/generators/templates/csrf_cookie_enabled.rb
|
50
52
|
- lib/react_router_rails_spa/generators/templates/home.tsx
|
51
53
|
- lib/react_router_rails_spa/generators/templates/proxy.ts
|
52
54
|
- lib/react_router_rails_spa/generators/templates/react-router.config.ts
|
@@ -83,5 +85,5 @@ requirements: []
|
|
83
85
|
rubygems_version: 3.4.10
|
84
86
|
signing_key:
|
85
87
|
specification_version: 4
|
86
|
-
summary:
|
88
|
+
summary: Integrate React Router v7 SPA framework with your Ruby on Rails backend.
|
87
89
|
test_files: []
|
data/.idea/.name
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
re_rou_ra
|